Node.js+Express+Multer文件上传问题:前端上传后端接收到undefined
前端上传文件到Node.js服务器时后端无法接收文件的问题
问题描述
尝试从前端向Node.js服务器上传文件,后端使用multer处理,但req.file始终为undefined。Postman测试上传正常,但前端上传失败,怀疑是Content-Type相关问题。
相关代码
HTML代码
<form id="file-upload-form" class="uploader" action="/upload" method="post" enctype="multipart/form-data" > <input id="file-upload" type="file" name="fileUpload" accept=".txt, .doc, .docx, .odt, .xls, .xlsx, .ods, .ppt, .pptx, .odp, .pdf, .jpg, .jpeg, .png, .gif" /> <label for="file-upload" id="file-drag"> <img id="file-image" src="#" alt="Preview" class="hidden" /> <img id="document-icon" src="#" alt="Document Icon" class="hidden" /> <div id="start"> <img src="assets/download.png" alt="upload" /> <div> <span class="selectText">Select a file or drag here</span> </div> <div id="notimage" class="hidden">Please select an image</div> <span id="file-upload-btn" class="btn btn-primary" >Select a file</span > </div> <div id="response" class="hidden"> <div id="messages"></div> <progress class="progress" id="file-progress" value="0"> <span>0</span>% </progress> </div> </label> </form>
前端JavaScript代码(原文件验证部分)
function uploadFile(file) { let xhr = new XMLHttpRequest(), fileInput = document.getElementById("class-roster-file"), pBar = document.getElementById("file-progress"), fileSizeLimit = 15; // In MB if (xhr.upload) { // Check if file is less than x MB if (file.size <= fileSizeLimit * 1024 * 1024) { // Progress bar pBar.style.display = "inline"; xhr.upload.addEventListener("loadstart", setProgressMaxValue, false); xhr.upload.addEventListener("progress", updateFileProgress, false); // File received / failed xhr.onreadystatechange = function (e) { if (xhr.readyState == 4) { // Everything is good! document.getElementById("file-progress").className = xhr.status == 200 ? "success" : "failure"; // document.location.reload(true); } }; // Start upload xhr.open( "POST", document.getElementById("file-upload-form").action, true ); xhr.send(file); } else { output("Please upload a smaller file (< " + fileSizeLimit + " MB)."); } } }
后端Node.js代码
// Set up Multer for handling file uploads const storage = multer.diskStorage({ destination: function (req, file, cb) { cb(null, "submited_documents/"); }, filename: function (req, file, cb) { const fileName = file.originalname.split("."); cb( null, `${fileName[0]}-${Math.floor(100 + Math.random() * 900)}${generateRandomString(3)}.${fileName[1]}` ); }, }); const upload = multer({ storage: storage }); // Endpoint for handling file uploads app.post("/upload", upload.single("fileUpload"), (req, res) => { const uploadedFile = req.file; res.json(req.file); });
问题原因及解决方案
核心问题
- 直接发送File对象不符合multipart/form-data格式:后端multer需要解析符合multipart/form-data规范的请求体,直接调用
xhr.send(file)只会发送原始文件数据,不会添加正确的表单字段和边界标识,导致multer无法识别。 - DOM元素ID不匹配:前端代码中
document.getElementById("class-roster-file")对应的ID在HTML中不存在,实际input的ID是file-upload,这个错误虽不直接导致上传失败,但会引发后续潜在问题。
修正后的前端JavaScript代码
function uploadFile(file) { let xhr = new XMLHttpRequest(), fileInput = document.getElementById("file-upload"), // 修正ID匹配问题 pBar = document.getElementById("file-progress"), fileSizeLimit = 15; // In MB if (xhr.upload) { // 检查文件大小 if (file.size <= fileSizeLimit * 1024 * 1024) { // 初始化进度条 pBar.style.display = "inline"; xhr.upload.addEventListener("loadstart", setProgressMaxValue, false); xhr.upload.addEventListener("progress", updateFileProgress, false); // 处理响应 xhr.onreadystatechange = function (e) { if (xhr.readyState == 4) { document.getElementById("file-progress").className = xhr.status == 200 ? "success" : "failure"; } }; // 构造FormData,包装文件并匹配后端字段名 const formData = new FormData(); formData.append("fileUpload", file); // 字段名和后端upload.single("fileUpload")一致 // 发送请求,无需手动设置Content-Type,浏览器会自动处理 xhr.open("POST", document.getElementById("file-upload-form").action, true); xhr.send(formData); } else { output(`Please upload a smaller file (< ${fileSizeLimit} MB).`); } } }
关键说明
- 使用
FormData对象封装文件是符合multipart/form-data规范的必要步骤,必须确保append的字段名和后端upload.single("fileUpload")中的字段名完全一致。 - 不要手动设置
xhr.setRequestHeader("Content-Type", "multipart/form-data"),因为浏览器会自动添加包含正确边界(boundary)的Content-Type头,手动设置会丢失边界信息,导致multer无法解析。
内容的提问来源于stack exchange,提问作者Sam Caut
相关产品推荐
相关产品推荐

