利用Vue3+Nodejs实现文件上传入门篇
前言
最近在复习八股, 内容挺多的, 但是只看不练也没办法真正体会到八股的魅力, 今天来实现一个简易版的文件上传, 可能开始会比较粗糙简单, 但是里面的总结都是实践出来的, 让你深入体会到前后端是如何协作的
1.1 核心概念
什么是 multipart/form-data?`
当我们上传文件时,HTTP 请求需要使用 multipart/form-data 编码方式。这种方式可以将文件数据和普通表单字段一起发送。
请求标头的Content-Type字段如图所示:

1.2 前端实现
在前端 JavaScript 中,我们利用 FormData 来包装文件进行参数传输, FormData 不是一个普通的对象, 而是浏览器提供的一个Web API, 当服务端识别到前端传了这个对象的时候, 就会生成boundary分界线, 并且动态生成一串随机性极高的字符串
在发送请求时,不要手动设置请求头。 浏览器检测到 FormData 实例后,会自动配置正确的 Content-Type 并附带必要的 boundary 参数。
/**
* 基础文件上传
*/
const uploadFile = async () => {
if (!selectedFile.value) {
message.value = '请先选择文件'
return
}
const formData = new FormData()
formData.append('file', selectedFile.value)
try {
message.value = '正在上传...'
const res = await axios.post('http://localhost:3000/upload', formData)
console.log(res.data)
message.value = `上传成功: ${res.data.filename}`
} catch (error) {
console.log(error)
message.value = '上传失败'
}
}
重要提示:文件上传最好使用使用 axios 这个第三方库, 因为 axios 目前的技术在文件上传上很成熟, 可以解决请求头的配置并且可以不传formData对象就可以实现文件上传, fetch虽然是浏览器自带的API, 但是目前还是有部分瑕疵, 如果你在上传文件的时候特意配置了请求头multipart/form-data, 你们浏览器就会以为开发者已经解决了文件上传的问题, 就不会主动拼接 boundary
1.3 后端实现
原生的nodejs接收前端传来的文件是以二进制流的形式进行的, 直接处理流是非常复杂的,通常引入中间件 multer 来处理, multer可以自动匹配boundary, 不需要通过正则去匹配, 可能会导致文件损坏, multer可以将二进制文件和普通数据进行分离, 有利于后端对数据进行存储和修改
multer最早提供了一个配置简单的dest, 但是它带来了三个痛点
- 文件名无后缀并且文件名是随机生成的哈希值根本看不懂分不清, 并且储存的数据也不能直接打开, 乱码
- 只能存储在同一个文件夹下
- http协议默认使用Latin1解析, 导致文件名变成了???
multer.diskStorage是一个存储引擎, 它提供了两个核心的回调函数, 让你在文件写入硬盘的最后时刻进行拦截和修改
- destination, 控制文件的去向, 可以自定义将文件分开排放
- filename, 提取文件的后缀名并且拼接, 利用buffer修复乱码, 并且可以使用随机字符串来方式同文件覆盖
const express = require("express");
const app = express();
const cors = require("cors");
// 引入multer
const multer = require("multer");
const fs = require("fs");
const path = require("path");
// 修复cors中间件错误
app.use(cors());
// 文件保存失败
const uploadDir = path.join(__dirname, "uploads");
if (!fs.existsSync(uploadDir)) {
fs.mkdirSync(uploadDir);
}
// 这种方式只能仿覆盖,文件后缀名丢失,无法修复乱码问题
// const upload = multer({
// dest: uploadDir,
// });
// 配置 Multer 存储引擎
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, uploadDir);
},
filename: function (req, file, cb) {
// 修复文件名乱码问题
// Multer 处理 header 时默认用 latin1,需转回 utf8
const originalName = Buffer.from(file.originalname, "latin1").toString(
"utf8",
);
// 加上时间戳防止同名文件覆盖
cb(null, Date.now() + "-" + originalName);
},
});
// 这种方式能仿覆盖,文件后缀名完整,修复乱码问题
const upload = multer({ storage: storage });
app.post("/upload", upload.single("file"), (req, res) => {
res.json({
msg: "上传成功",
filename: req.file.filename,
});
});
app.listen(3000, () => {
console.log("Server started on port 3000");
});
// 设置超时时间
server.timeout = 600000;
通过 multer 和 multer.diskStorage 进行配置存储引擎可以实现文件名保存,同一个文件的上传,区别如下


1.4 Vue 组件示例
在视图层,我们需要处理文件选择、上传状态(Loading)以及结果展示三个核心逻辑。
这段代码展示了如何通过 ref 获取 DOM 元素,并在异步操作期间锁定按钮状态,防止用户重复提交:
<script setup lang="ts">
import axios from 'axios'
import { ref } from 'vue'
const selectedFile = ref(null)
const message = ref('')
const uploadFileChange = (e) => {
selectedFile.value = e.target.files[0]
message.value = ''
}
const uploadFile = async () => {
if (!selectedFile.value) {
message.value = '请先选择文件'
return
}
const formData = new FormData()
formData.append('file', selectedFile.value)
try {
message.value = '正在上传...'
const res = await axios.post('http://localhost:3000/upload', formData)
console.log(res.data)
message.value = `上传成功: ${res.data.filename}`
} catch (error) {
console.log(error)
message.value = '上传失败'
}
}
</script>
<template>
<div>
<input type="file" @change="uploadFileChange" />
<button @click="uploadFile">文件上传</button>
<p v-if="message">{{ message }}</p>
</div>
</template>
<style scoped>
div {
cursor: pointer;
}
</style>
1.5 常见 Bug 与解决方案
| Bug 现象 | 原因分析 | 解决方案 |
|---|---|---|
| 请求体为空,后端收不到文件 | 手动设置了 Content-Type 请求头 | 删除手动设置的 Content-Type,让浏览器自动处理 |
| 大文件上传超时 | 默认超时时间太短 | 配置更长的超时时间,或使用分片上传 |
| CORS 跨域错误 | 前后端不同源 | 后端配置 CORS 中间件 app.use(cors()) |
总结
到此这篇关于利用Vue3+Nodejs实现文件上传入门篇的文章就介绍到这了,更多相关Vue3+Nodejs文件上传内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
ElementUI中el-dropdown-item点击事件无效问题
这篇文章主要介绍了ElementUI中el-dropdown-item点击事件无效问题及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教2023-04-04


最新评论