Vue 中实现 PDF 文件上传
在 Vue 中实现 PDF 文件上传,核心是通过文件选择控件获取 PDF 文件,验证文件合法性(类型、大小等),再通过 axios 提交到后端接口。结合你的项目(使用 Element UI),以下是完整实现方案:
一、基础方案:使用 Element UI Upload 组件(推荐)
Element UI 的 el-upload 组件封装了文件选择、验证、上传等逻辑,使用简单且样式统一,适合快速集成。
1. 组件代码(单文件上传)
<template><div class="pdf-upload"><!-- 上传按钮 --><el-uploadclass="upload-demo"action="" <!-- 不直接用action,改为手动上传 -->:auto-upload="false" <!-- 关闭自动上传,手动触发 -->:on-change="handleFileChange" <!-- 文件选择变化时触发 -->:accept=".pdf" <!-- 限制只能选择PDF文件 -->:file-list="fileList" <!-- 已选择的文件列表 -->:limit="1" <!-- 限制只能上传1个文件 -->:on-exceed="handleExceed" <!-- 超过数量限制时触发 -->><el-button size="small" type="primary">选择 PDF 文件</el-button><div slot="tip" class="el-upload__tip">只能上传 PDF 格式文件,且大小不超过 10MB</div></el-upload><!-- 上传按钮 --><el-button size="small" type="success" @click="handleUpload" :disabled="!fileList.length">上传文件</el-button></div>
</template><script>
import api from '@/api' // 引入封装的axios接口export default {data() {return {fileList: [] // 存储选择的文件}},methods: {// 文件选择变化时触发(验证文件)handleFileChange(file, fileList) {this.fileList = fileList.slice(-1) // 只保留最后选择的1个文件(配合limit=1)// 验证文件类型if (file.raw.type !== 'application/pdf') {this.$message.error('请上传 PDF 格式的文件!')this.fileList = [] // 清空无效文件return false}// 验证文件大小(10MB = 10 * 1024 * 1024 bytes)const maxSize = 10 * 1024 * 1024if (file.size > maxSize) {this.$message.error('文件大小不能超过 10MB!')this.fileList = [] // 清空无效文件return false}},// 超过文件数量限制时触发handleExceed(files, fileList) {this.$message.warning(`最多只能上传 1 个 PDF 文件`)},// 手动触发上传async handleUpload() {if (!this.fileList.length) returnconst file = this.fileList[0].raw // 获取原生文件对象// 构造 FormData(文件上传必须用 FormData 格式)const formData = new FormData()formData.append('pdfFile', file) // 'pdfFile' 对应后端接口的参数名// 如需额外参数,可继续 append,例如:// formData.append('userId', 123)try {// 调用上传接口(需在 api 中定义)const res = await api.file.uploadPdf(formData)this.$message.success('文件上传成功!')console.log('上传结果:', res)// 上传成功后清空文件列表this.fileList = []} catch (error) {this.$message.error('文件上传失败,请重试!')console.error('上传错误:', error)}}}
}
</script><style scoped>
.pdf-upload {margin: 20px;
}
.el-upload__tip {margin-top: 10px;
}
</style>
2. 封装上传接口(src/api/file.js)
import request from '@/utils/request'export default {// 上传 PDF 文件uploadPdf(formData) {return request({url: '/api/file/upload-pdf', // 后端上传接口地址method: 'post',data: formData, // 直接传递 FormData 对象// 上传进度配置(可选)onUploadProgress: progressEvent => {const percent = Math.round((progressEvent.loaded / progressEvent.total) * 100)console.log(`上传进度:${percent}%`)// 可在这里更新进度条状态}})}
}
二、原生方案:使用 input [type=“file”](不依赖 UI 组件)
如果不使用 Element UI,可直接用原生 input 控件实现,核心逻辑一致:
<template><div class="native-upload"><input type="file" accept=".pdf" @change="handleFileSelect" class="file-input"><button @click="handleUpload" :disabled="!selectedFile">上传 PDF</button><p v-if="uploadProgress > 0 && uploadProgress < 100">上传中:{{ uploadProgress }}%</p></div>
</template><script>
import axios from 'axios'export default {data() {return {selectedFile: null,uploadProgress: 0}},methods: {// 选择文件handleFileSelect(e) {const file = e.target.files[0]if (!file) return// 验证文件类型if (file.type !== 'application/pdf' && !file.name.endsWith('.pdf')) {alert('请选择 PDF 格式文件!')e.target.value = '' // 清空选择return}// 验证文件大小(10MB)if (file.size > 10 * 1024 * 1024) {alert('文件大小不能超过 10MB!')e.target.value = ''return}this.selectedFile = file},// 上传文件async handleUpload() {if (!this.selectedFile) returnconst formData = new FormData()formData.append('pdfFile', this.selectedFile)try {const res = await axios.post('/api/file/upload-pdf', formData, {onUploadProgress: progress => {this.uploadProgress = Math.round((progress.loaded / progress.total) * 100)}})alert('上传成功!')this.selectedFile = nullthis.uploadProgress = 0} catch (error) {alert('上传失败!')console.error(error)}}}
}
</script><style>
.file-input {margin: 10px 0;
}
button {padding: 6px 12px;background: #42b983;color: white;border: none;border-radius: 4px;cursor: pointer;
}
button:disabled {background: #ccc;cursor: not-allowed;
}
</style>
三、关键注意事项
-
文件类型验证:
- 通过
file.type === 'application/pdf'验证 MIME 类型,但部分环境可能不准确,建议同时检查文件名后缀file.name.endsWith('.pdf')。
- 通过
-
文件大小限制:后端通常也会有大小限制,前端验证仅作为友好提示,需和后端保持一致(例如 10MB)。
-
FormData 格式:文件上传必须使用
FormData包装,且 axios 会自动设置Content-Type: multipart/form-data,不要手动设置此请求头(否则可能导致后端解析失败)。 -
Electron 环境特殊处理:
-
如果需要直接读取本地文件路径(非通过 input 选择),可使用 Electron 的
dialog.showOpenDialog选择文件:const { remote } = require('electron') const { dialog } = remote// 手动选择文件(不通过 input) async selectFileManually() {const result = await dialog.showOpenDialog({filters: [{ name: 'PDF Files', extensions: ['pdf'] }], // 只显示 PDFproperties: ['openFile']})if (!result.canceled) {const filePath = result.filePaths[0]// 读取文件并转换为 Blob 后上传(需用 fs 模块)const fs = require('fs')const fileBuffer = fs.readFileSync(filePath)const file = new Blob([fileBuffer], { type: 'application/pdf' })this.selectedFile = file} }
-
四、后端接口要求
后端接口需支持 multipart/form-data 类型请求,以 Node.js(Express)为例,需使用 multer 中间件处理文件:
const express = require('express')
const multer = require('multer')
const app = express()// 配置上传目录和文件名
const storage = multer.diskStorage({destination: (req, file, cb) => cb(null, 'uploads/'),filename: (req, file, cb) => cb(null, Date.now() + '-' + file.originalname)
})
const upload = multer({ storage,limits: { fileSize: 10 * 1024 * 1024 }, // 限制10MBfileFilter: (req, file, cb) => {// 后端再次验证文件类型if (file.mimetype !== 'application/pdf') {return cb(new Error('只允许上传 PDF 文件'))}cb(null, true)}
})// 上传接口
app.post('/api/file/upload-pdf', upload.single('pdfFile'), (req, res) => {res.json({code: 200,message: '上传成功',data: { filePath: req.file.path }})
})
通过以上方案,可在 Vue 中快速实现 PDF 文件上传功能,结合你的 Electron + Vue 项目场景,按需选择 Element 组件或原生实现即可。
