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

news2025/3/3 14:36:45

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

文章目录

    • 一、环境准备
      • 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.coloradmin.cn/o/2308430.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【云原生】SpringCloud-Spring Boot Starter使用测试

目录 Spring Boot Starter是什么&#xff1f; 以前传统的做法 使用 Spring Boot Starter 之后 starter 的理念&#xff1a; starter 的实现&#xff1a; ?创建Spring Boot Starter步骤 在idea新建一个starter项目、直接执行下一步即可生成项目。 ?在xml中加入如下配置…

介绍下pdf打印工具类 JasperPrint

JasperPrint 工具类深度解析 JasperPrint 是 JasperReports 框架中实现 PDF 打印的核心载体类&#xff0c;其本质是 填充数据后的可打印报表对象&#xff0c;承担着从模板编译、数据填充到格式输出的全流程控制。以下从 7 个维度展开深度解析&#xff1a; 一、核心定位与生命周…

idea中或pycharm中编写Markdown文件

参考 ltjt_aiseek: seek_backend_py 项目 数智科技ai探索API接口开发 1. 安装 Django 框架 在开始创建 Django 项目之前&#xff0c;需要先安装 Django 框架。可以通过 PyCharm 的终端或者系统的命令行工具来完成安装。 使用 PyCharm 终端安装 打开 PyCharm&#xff0c;如果…

Go红队开发—并发编程

文章目录 并发编程go协程chan通道无缓冲通道有缓冲通道创建⽆缓冲和缓冲通道 等协程sync.WaitGroup同步Runtime包Gosched()Goexit() 区别 同步变量sync.Mutex互斥锁atomic原子变量 SelectTicker定时器控制并发数量核心机制 并发编程阶段练习重要的细节端口扫描股票监控 并发编程…

使用自动化运维工具 Ansible 集中化管理服务器

一、概述 Ansible 是一款为类 Unix 系统开发的自由开源的配置和自动化工具 官方网站:https://www.ansible.com/ Ansible 成立于 2013 年,总部设在北卡罗来纳州达勒姆,联合创始人 ad Ziouani 和高级副总裁 Todd Barr都是红帽的老员工。Ansible 旗下的开源软件 Ansible 十分…

数据集笔记:新加坡 一些交通的时间序列统计量

1 机动车年度保有量 data.gov.sg 各类机动车年度保有量 数据范围&#xff1a;2005年1月 - 2020年12月 1.1 数据说明 非高峰时段车辆 包括周末车&#xff08;Weekend Cars&#xff09;和 修订版非高峰时段车辆&#xff08;Revised Off Peak Cars&#xff09;&#xff0c;该…

【FL0090】基于SSM和微信小程序的球馆预约系统

&#x1f9d1;‍&#x1f4bb;博主介绍&#x1f9d1;‍&#x1f4bb; 全网粉丝10W,CSDN全栈领域优质创作者&#xff0c;博客之星、掘金/知乎/b站/华为云/阿里云等平台优质作者、专注于Java、小程序/APP、python、大数据等技术领域和毕业项目实战&#xff0c;以及程序定制化开发…

智能图像处理平台:图像处理配置类

这里我们先修改一下依赖&#xff0c;不用JavaCV&#xff0c;用openCV。 导入依赖&#xff1a; <!-- JavaCV 依赖&#xff0c;用于图像和视频处理 --> <!-- <dependency>--> <!-- <groupId>org.bytedeco</groupId>--> &l…

一周一个Unity小游戏2D反弹球游戏 - 球板的发球

前言 本文将实现当游戏开始时球在球板上,且不具备物理性,在Windows平台上通过点击屏幕来球发射,安卓平台上当手指触摸到屏幕上时进行发球,并此时开始具备物理性。 发球逻辑 首先在球板上创建一个球的发射点,新建一个空的游戏物体,并命名为BallPoint,并将其作为SpringBoa…

012 rocketmq事务消息

文章目录 事务消息概念介绍交互流程事务消息原理TransactionListener接⼝TransactionProducer.javaTransactionConsumer.java 事务消息 内置topic中的消息对消费者不可见 本地事务mq消息事务消息 消息队列 RocketMQ 版提供的分布式事务消息适⽤于所有对数据最终⼀致性有强需求…

SpringBoot原理-02.自动配置-概述

一.自动配置 所谓自动配置&#xff0c;就是Spring容器启动后&#xff0c;一些配置类、bean对象就自动存入了IOC容器当中&#xff0c;而不需要我们手动声明&#xff0c;直接从IOC容器中引入即可。省去了繁琐的配置操作。 我们可以首先将spring项目启动起来&#xff0c;里面有一…

知识图谱+智能问诊预诊系统vue+django+neo4j架构、带问诊历史

文章结尾部分有CSDN官方提供的学长 联系方式名片 文章结尾部分有CSDN官方提供的学长 联系方式名片 关注B站&#xff0c;有好处&#xff01; &#x1f90d;编号&#xff1a;D032 &#x1f90d;智能问答&#xff1a;智能问答自诊、预诊功能&#xff0c;同时可以保存问答历史 &…

DeepSeek 助力 Vue3 开发:打造丝滑的悬浮按钮(Floating Action Button)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…

Java数据结构_一篇文章了解常用排序_8.1

本文所有排序举例均默认为升序排列。 目录 1. 常见的排序算法 2. 常见排序算法的实现 2.1 插入排序 2.1.1 基本思想&#xff1a; 2.1.2 直接插入排序 2.1.3 希尔排序&#xff08;缩小增量排序&#xff09; 2.2 选择排序 2.2.1 基本思想&#xff1a; 2.2.2 直接选择排…

(南京观海微电子)——倍压设计与应用

在电路设计过程中&#xff0c;当后级需要的电压比前级高出数倍而所需要的电流并不是很大时&#xff0c;就可以使用倍压整流电路。倍压整流&#xff1a;可以将较低的交流电压&#xff0c;用耐压较高的整流二极管和电容器&#xff0c;“整”出一个较高的直流电压。 01 倍压整流电…

网络安全-使用DeepSeek来获取sqlmap的攻击payload

文章目录 概述DeepSeek使用创建示例数据库创建API测试sqlmap部分日志参考 概述 今天来使用DeepSeek做安全测试&#xff0c;看看在有思路的情况下实现的快不快。 DeepSeek使用 我有一个思路&#xff0c;想要测试sqlmap工具如何dump数据库的&#xff1a; 连接mysql数据库&#…

【含文档+PPT+源码】基于SpringBoot+Vue医药知识学习与分享平台的设计与实现

项目介绍 本课程演示的是一款 基于SpringBootVue医药知识学习与分享平台的设计与实现&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的 Java 学习者。 1.包含&#xff1a;项目源码、项目文档、数据库脚本、软件工具等所有资料 2.带你从零开始部署…

coze生成的工作流,发布后,利用cmd命令行执行。可以定时发日报,周报等。让他总结你飞书里面的表格。都可以

coze生成的工作流&#xff0c;发布后&#xff0c;利用cmd命令行执行。可以定时发日报&#xff0c;周报等。让他总结你飞书里面的表格。都可以。 很简单。 准备工作&#xff0c;先发布你的工作流&#xff0c;和发布应用。 然后&#xff0c;点击扣子API 。 申请一个&#xff0…

【Python】基础语法三

> 作者&#xff1a;დ旧言~ > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;了解Python的函数、列表和数组。 > 毒鸡汤&#xff1a;有些事情&#xff0c;总是不明白&#xff0c;所以我不会坚持。早安! > 专栏选自&#xff…

使用 Spring Boot 和 Keycloak 的 OAuth2 快速指南

1. 概述 本教程是关于使用 Spring Boot 和 Keycloak 通过 OAuth2 配置后端的。 我们将使用 Keycloak 作为 OpenID 提供程序。我们可以将其视为负责身份验证和用户数据&#xff08;角色、配置文件、联系信息等&#xff09;的用户服务。它是最完整的 OpenID Connect &#xff0…