当前位置: 首页 > article >正文

Vue3实现文件上传、下载及预览全流程详解(含完整接口调用)

在这里插入图片描述
在这里插入图片描述

文章目录

    • 一、环境准备
      • 1.1 创建Vue3项目
      • 1.2 安装依赖
      • 1.3 配置Element Plus
    • 二、文件上传实现
      • 2.1 基础上传组件
      • 2.2 自定义上传逻辑(Axios实现)
    • 三、文件下载实现
      • 3.1 直接下载(已知文件URL)
      • 3.2 后端接口下载(二进制流)
    • 四、文件预览实现
      • 4.1 图片预览
      • 4.2 PDF预览(使用pdf.js)
      • 4.3 Excel预览(使用xlsx)
    • 五、完整接口示例
      • 5.1 上传接口(Node.js示例)
      • 5.2 下载接口(Node.js示例)
    • 六、注意事项
    • 七、完整项目结构
    • 八、总结

一、环境准备

1.1 创建Vue3项目

npm create vue@latest
# 选择TypeScript、Pinia(可选)、ESLint等配置

1.2 安装依赖

npm install axios element-plus @element-plus/icons-vue

1.3 配置Element Plus

// main.ts
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'

const app = createApp(App)
app.use(ElementPlus)

二、文件上传实现

2.1 基础上传组件

<template>
  <el-upload
    class="upload-demo"
    :action="uploadUrl"
    :headers="headers"
    :on-success="handleSuccess"
    :before-upload="beforeUpload"
  >
    <el-button type="primary">点击上传</el-button>
    <template #tip>
      <div class="el-upload__tip">支持扩展名:.jpg/.png/.pdf,最大5MB</div>
    </template>
  </el-upload>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import type { UploadProps } from 'element-plus'

const uploadUrl = ref('https://api.example.com/upload')
const headers = ref({
  Authorization: 'Bearer ' + localStorage.getItem('token')
})

// 文件预校验
const beforeUpload: UploadProps['beforeUpload'] = (rawFile) => {
  const isValidType = ['image/jpeg', 'image/png', 'application/pdf'].includes(rawFile.type)
  const isLt5M = rawFile.size / 1024 / 1024 < 5
  
  if (!isValidType) {
    ElMessage.error('文件格式不支持!')
    return false
  }
  if (!isLt5M) {
    ElMessage.error('文件大小不能超过5MB!')
    return false
  }
  return true
}

// 上传成功回调
const handleSuccess: UploadProps['onSuccess'] = (response) => {
  ElMessage.success('上传成功')
  console.log('服务器返回:', response)
  // 通常返回文件访问地址
}
</script>

2.2 自定义上传逻辑(Axios实现)

const customUpload = async (file: File) => {
  const formData = new FormData()
  formData.append('file', file)
  formData.append('userId', '123')

  try {
    const { data } = await axios.post('/api/upload', formData, {
      headers: {
        'Content-Type': 'multipart/form-data',
        Authorization: `Bearer ${localStorage.getItem('token')}`
      }
    })
    return data
  } catch (error) {
    ElMessage.error('上传失败')
    throw error
  }
}

三、文件下载实现

3.1 直接下载(已知文件URL)

<template>
  <el-button @click="handleDownload">下载文件</el-button>
</template>

<script setup lang="ts">
const handleDownload = () => {
  const link = document.createElement('a')
  link.href = 'https://api.example.com/files/sample.pdf'
  link.download = 'filename.pdf' // 设置下载文件名
  document.body.appendChild(link)
  link.click()
  document.body.removeChild(link)
}
</script>

3.2 后端接口下载(二进制流)

const downloadFile = async (fileId: string) => {
  try {
    const response = await axios.get(`/api/download/${fileId}`, {
      responseType: 'blob'
    })

    // 创建Blob对象
    const blob = new Blob([response.data])
    const url = window.URL.createObjectURL(blob)
    
    // 提取文件名
    const contentDisposition = response.headers['content-disposition']
    const fileName = contentDisposition
      ?.split('filename=')[1]
      ?.replace(/"/g, '')
      || 'download-file'

    // 创建下载链接
    const link = document.createElement('a')
    link.href = url
    link.download = fileName
    document.body.appendChild(link)
    link.click()
    window.URL.revokeObjectURL(url)
    document.body.removeChild(link)
  } catch (error) {
    ElMessage.error('下载失败')
  }
}

四、文件预览实现

4.1 图片预览

<template>
  <el-image 
    :src="imageUrl" 
    :preview-src-list="[imageUrl]"
    fit="cover"
  />
</template>

4.2 PDF预览(使用pdf.js)

npm install pdfjs-dist @types/pdfjs-dist
<template>
  <div ref="pdfContainer" class="pdf-viewer"></div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import * as pdfjsLib from 'pdfjs-dist'

const props = defineProps<{
  pdfUrl: string
}>()

const pdfContainer = ref<HTMLElement>()

onMounted(async () => {
  const loadingTask = pdfjsLib.getDocument(props.pdfUrl)
  const pdf = await loadingTask.promise
  
  for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
    const page = await pdf.getPage(pageNum)
    const viewport = page.getViewport({ scale: 1.5 })
    
    const canvas = document.createElement('canvas')
    const context = canvas.getContext('2d')!
    canvas.height = viewport.height
    canvas.width = viewport.width
    
    await page.render({
      canvasContext: context,
      viewport: viewport
    }).promise
    
    pdfContainer.value?.appendChild(canvas)
  }
})
</script>

4.3 Excel预览(使用xlsx)

npm install xlsx
<template>
  <el-table :data="excelData">
    <el-table-column 
      v-for="(col, index) in columns"
      :key="index"
      :prop="col"
      :label="col"
    />
  </el-table>
</template>

<script setup lang="ts">
import { read, utils } from 'xlsx'
import { ref } from 'vue'

const excelData = ref([])
const columns = ref([])

const previewExcel = async (file: File) => {
  const data = await file.arrayBuffer()
  const workbook = read(data)
  const worksheet = workbook.Sheets[workbook.SheetNames[0]]
  const jsonData = utils.sheet_to_json(worksheet, { header: 1 })
  
  columns.value = jsonData[0]
  excelData.value = jsonData.slice(1).map(row => {
    return columns.value.reduce((obj, col, index) => {
      obj[col] = row[index]
      return obj
    }, {})
  })
}
</script>

五、完整接口示例

5.1 上传接口(Node.js示例)

// Express 路由
app.post('/api/upload', (req, res) => {
  const multer = require('multer')
  const upload = multer({ dest: 'uploads/' })
  
  upload.single('file')(req, res, (err) => {
    if (err) return res.status(500).json({ code: 500, message: '上传失败' })
    
    // 返回文件信息
    res.json({
      code: 200,
      data: {
        url: `/files/${req.file.filename}`,
        originalname: req.file.originalname,
        size: req.file.size
      }
    })
  })
})

5.2 下载接口(Node.js示例)

app.get('/api/download/:filename', (req, res) => {
  const filePath = path.join(__dirname, 'uploads', req.params.filename)
  
  res.setHeader('Content-Type', 'application/octet-stream')
  res.setHeader('Content-Disposition', `attachment; filename=${req.params.filename}`)
  
  const fileStream = fs.createReadStream(filePath)
  fileStream.pipe(res)
})

六、注意事项

  1. 安全验证:所有文件接口需进行身份验证
  2. 文件大小限制:Nginx需配置client_max_body_size
  3. 文件存储
    • 敏感文件不要存储在公开目录
    • 使用OSS云存储更佳
  4. 预览安全
    • 防止XSS攻击(特别处理HTML文件)
    • 使用CSP内容安全策略

七、完整项目结构

/src
├─api/
│  └─file.ts       # 文件相关接口封装
├─components/
│  └─FilePreview.vue # 文件预览组件
├─utils/
│  ├─download.ts   # 下载工具函数
│  └─validation.ts # 文件验证函数

八、总结

本文全面而详尽地实现了多项关键功能,为开发者提供了从前端到后端、从组件开发到接口对接的全方位解决方案。首先,我们基于Element Plus这一流行的Vue3组件库,成功构建了一个高效且用户友好的文件上传组件。该组件不仅支持文件的快速上传,还提供了丰富的用户交互体验,如进度条显示、上传成功/失败提示等,极大地提升了用户的使用感受。

在文件处理方面,我们实现了Axios二进制流文件的下载功能。通过Axios的强大网络请求能力,我们能够轻松地从服务器获取二进制文件流,并将其保存到本地或进行进一步的处理。这一功能为文件的异步下载和动态处理提供了有力支持。

为了满足不同格式文件的预览需求,我们精心设计了一套PDF/Excel/图片多格式的预览方案。该方案能够自动识别文件类型,并调用相应的预览组件进行展示。无论是常见的图片文件,还是复杂的PDF、Excel文档,都能在我们的系统中得到清晰、准确的预览效果。

此外,我们还提供了前后端完整接口示例,展示了如何在Vue3前端与后端服务器之间进行高效的数据交互。通过详细的接口定义和示例代码,开发者可以快速地理解并实现前后端的数据通信,为项目的快速迭代和部署提供了有力保障。

最后,在生产环境注意事项部分,我们总结了在实际部署过程中可能遇到的问题及解决方案,如性能优化、安全性考虑等。这些宝贵的经验分享将帮助开发者更好地将项目从开发环境迁移到生产环境,确保系统的稳定性和可靠性。综上所述,本文不仅提供了丰富的功能实现,还为开发者提供了全面的开发指导和实战经验分享。


http://www.kler.cn/a/568265.html

相关文章:

  • 加油站小程序实战教程03站点管理
  • 《从0到1:用Python在鸿蒙系统开发安防图像分类AI功能》
  • Ubuntu 下 nginx-1.24.0 源码分析 - ngx_pstrdup函数
  • 计算机视觉(opencv-python)入门之图像的读取,显示,与保存
  • 【每日八股】MySQL篇(三):索引(上)
  • 如何让 Git 管理本地项目
  • 基于PHP+MySQL实现的毕业设计选题管理系统
  • 算法(四)——位运算与位图
  • Unity中动态切换光照贴图的方法
  • Android限制后台服务、广播和Activity,节省更多的电量
  • MAC 怎么设置 Java虚拟内存设置
  • vue+wsplayer对接大华的rtsp实时预览视频流
  • LangChain解锁LLM大语言模型的结构化输出能力:调用 with_structured_output() 方法
  • ORM Bee V2.5.2.x 发布,支持 CQRS; sql 性能分析;更新 MongoDB ORM分片
  • 六十天前端强化训练之第五天响应式设计原理深度解析
  • 0301 leetcode - 1502.判断是否能形成等差数列、 682.棒球比赛、657.机器人能否返回原点
  • java数据结构_Map和Set_9.1
  • 【K8S】Kubernetes 基本架构、节点类型及运行流程详解(附架构图及流程图)
  • CES Asia 2025前瞻:网络安全与数据隐私成焦点
  • 在Linux上安装go环境