You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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);
});

问题原因及解决方案

核心问题

  1. 直接发送File对象不符合multipart/form-data格式:后端multer需要解析符合multipart/form-data规范的请求体,直接调用xhr.send(file)只会发送原始文件数据,不会添加正确的表单字段和边界标识,导致multer无法识别。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 10:28:14