使用Multer按用户指定文件名存文件异常:始终用默认名称
我在Express中集成Multer实现文件上传,希望按照前端传入的filename参数保存文件,但实际总是用默认名称存储。在multer.diskStorage的filename回调(标记为place2处)中打印req.body.filename显示undefined,req.body是空对象;但在/upload路由处理函数中却能获取到正确的req.body。请问这一现象的原因是什么?
const express = require("express"); const mongoose = require("mongoose"); const cors = require("cors"); const multer = require("multer"); const path = require("path"); process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0"; const app = express(); app.use(cors()); app.use(express.urlencoded({ extended: true })); // Parse URL-encoded bodies for form data mongoose.connect("mongodb://127.0.0.1:27017/Gallary"); const schema = new mongoose.Schema({ email: { required: true, type: String, }, password: { required: true, type: String, }, }); const Login = mongoose.model("Login", schema); app.get("/", (req, res) => { res.send("Gallery it is"); }); app.post("/SignUp", async (req, res) => { try { let result = await Login.find(req.body); if (result.length == 0) { let data = new Login(req.body); await data.save(); res.send({ result: true }); } else { res.send({ result: false }); } } catch (e) { res.send(e); } }); app.post("/Login", async (req, res) => { try { let result = await Login.find(req.body); console.log("came"); if (result.length != 0) { res.send({ result: true }); } else { res.send({ result: false }); } } catch (e) { res.send({ result: false }); } }); // Define multer storage configuration const storage = multer.diskStorage({ destination: (req, file, cb) => { cb(null, "uploads/"); }, filename: (req, file, cb) => { console.log("place2"+req.body.filename ); const filename = req.body.filename ? req.body.filename : `default_filename_${Date.now()}${path.extname(file.originalname)}`; cb(null, filename); }, }); const upload = multer({ storage: storage }); // Define route for image upload app.post("/upload", upload.single("image"), (req, res) => { console.log("Uploaded file:", req.file.filename); res.json({ filename: req.file.filename }); }); app.listen(process.env.PORT || 3001, () => { console.log("Server is running on port 3001"); });
核心原因
Multer处理文件上传的时机早于Express的express.urlencoded()中间件解析请求体的时机。
当前端发送multipart/form-data类型的请求时,请求体里的普通字段(比如filename)和文件数据是混合传输的。Multer在解析文件阶段会先读取文件部分,此时Express的express.urlencoded()还没开始解析普通表单字段,所以在filename回调里req.body还没被填充,自然拿不到filename参数。而进入路由处理函数时,Multer已经完成了整个请求体的解析(包括普通字段),所以此时req.body是完整的。
解决方案
Multer本身会自动解析multipart/form-data请求里的普通字段并填充到req.body中,不需要依赖express.urlencoded()(它只负责解析application/x-www-form-urlencoded类型的请求)。只需注意:
- 前端用FormData发送数据时,先添加普通字段,再添加文件。比如:
// 前端示例代码 const formData = new FormData(); formData.append('filename', '自定义文件名.jpg'); // 先传普通字段 formData.append('image', file); // 后传文件
这样Multer在解析文件前会先解析前面的filename字段,此时req.body.filename就能在filename回调中正常获取到了。
后端的express.urlencoded()可以保留,用于处理其他非文件上传的表单请求,不会影响Multer的工作。
内容的提问来源于stack exchange,提问作者Pratham Singh

