当文件以'multipart/form-data'形式发送时,Express Route返回状态代码500,并带有通用的'错误'。

2 人关注

我有一个Express路由,它被设置为接受 multipart/formdata 文件并将其上传到S3桶。我正在使用 multer 来过滤图片类型,并通过创建一个 upload 目录将它们暂时存储在服务器上。在上传成功后不久,这些文件就会被删除。根据multer的配置,文件数组被命名为 images ,最多可接受3张图片。

该代码在我的本地机器上完美地工作。我通过POSTMAN测试,可以上传1-3个文件并得到正确的响应。如果没有附加文件,也会触发正确的响应,都是状态代码200。

完全相同的代码库用Docker部署在Amazon ECS上,但不知何故一直失败,状态码是500,而且是代码库中找不到的一般 "错误 "信息。使用日志,我已经确定multer不是原因,因为它通过了过滤器。它似乎是在multer中间件和路由本身之间的某个地方失败了,有一个例外。

异常情况。使用POSTMAN,如果一个 multipart/formdata POST请求没有文件,即空的 images 数组,路由被正确触发,并返回信息 "你没有附加任何图像 "作为响应。

我一直无法解决这个问题,如果能在这个问题上提供一些指导,我将不胜感激!

CODE SNIPPETS。

filesController。

files.post(
  "/multiple",
  upload.array("images", 3),
  async (req: ExpressRequest, res: ExpressResponse) => {
    try {
      const files: { [fieldname: string]: Express.Multer.File[] } | Express.Multer.File[] =
        req.files;
      console.log("FILES", files);
      // execute only if there are files
if (files.length > 0) {
        const dataPromises = (files as Array<Express.Multer.File>).map(
          async (file: Express.Multer.File) => {
            // check if file.mimetype here is 'image/heic', and convert into jpeg accordingly
const fileNameWithoutExt = file.filename.split(".")[0];
            try {
              if (file.mimetype == "image/heic") {
                await convertFile(file, fileNameWithoutExt, 0.2);
                const response = await uploadFilePath(
                  S3_IMAGE_BUCKET,
                  `./uploads/${fileNameWithoutExt}.jpeg`,
                  `${fileNameWithoutExt}.jpeg`
                console.log("HEIC File Upload Response", response);
                fs.unlinkSync(`./uploads/${fileNameWithoutExt}.jpeg`);
                fs.unlinkSync(file.path);
                return {
                  fileName: `${fileNameWithoutExt}.jpeg`,
                  metaData: response.$metadata,
              } else {
                const response = await uploadFile(S3_IMAGE_BUCKET, file);
                console.log("JPEG File Upload Response", response);
                fs.unlinkSync(file.path);
                return {
                  fileName: file.filename,
                  metaData: response.$metadata,
            } catch (err) {
              console.error("Error for file conversion/upload", err, err.stack);
              res.status(500).send({
                message: "Upload failed due to conversion or something.",
                error: err,
                stack: err.stack,
        const fileData = await Promise.all(dataPromises);
        const fileNames = fileData.map((data: any) => data.fileName);
        const statusCodes = fileData.map((data: any) => data.metaData.httpStatusCode);
        if (statusCodes.find((statusCode) => statusCode === 200)) {
          res.status(200).send({
            filePath: `/image/`,
            fileNames,
        } else {
          res.status(403).send({
            message: "Upload failed. Please check credentials or file has been selected.",
      } else {
        res.status(200).send({
          message: "You did not attach any images",
    } catch (err) {
      res.status(500).send({
        message: "Upload failed. Please check credentials or file has been selected.",

multer配置。

const storage = multer.diskStorage({
  // potential error, path to store files, callback
destination: (req, file, cb) => {
    // cb acceptes two arguments: 1. err 2. destination folder wrt to server.js
cb(null, "uploads/");
  filename: (req, file, cb) => {
    console.log("MULTER STORAGE STARTED")
    const date = new Date().toISOString().substring(0, 10);
    // const name = `${req.body.first_name}_${req.body.last_name}`;
// cb defines the name of the file when stored
const alphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-";
    const nanoid = customAlphabet(alphabet, 20);
    cb(null, `${date}_${nanoid()}_${file.originalname}`);
    console.log("FILE NAME CREATED, MULTER STORAGE STOPPED")
/* Accept jpeg or png files only */
// NOTE: file type rejection works, but there is no error message displayed if file is rejected. logic in route continues to be executed
const fileFilter = (
  req: Request,
  file: Express.Multer.File,
  cb: (error: Error | null, accepted: boolean) => void
) => {
  console.log("======== FILE FILTER ========", file);
    file.mimetype === "image/jpeg" ||
    file.mimetype === "image/png" ||
    file.mimetype === "image/heic"
    cb(null, true);
    console.log("FILTER PASSED")
  } else {
    console.log("FILTER FAILED");
    cb(null, false);
/* Only accepts filesize up to 5MB */
// the first parameter is super important that determines where the data is stored on the server
const upload = multer({
  dest: "uploads/", // default simple config to upload file as binary data
  storage, // enable if storage of actual file is required.
// limits: { fileSize: 1024 * 1024 * 5 },
  fileFilter,

屏幕截图。

在表格数据中没有图像的响应

表单数据中带有图像的响应

3 个评论
eol
你尝试过调试它吗?
它发生在upload.array("images", 3)中间件之后,回调之前,因为console.log("FILES", files)不会被记录,如果请求中发送了图像。
eol
记录 != 调试 :)另外,这个错误的堆栈跟踪记录是怎么说的?
javascript
node.js
docker
express
amazon-ecs
jacob
jacob
发布于 2021-12-29
1 个回答
Mark Gaensicke
Mark Gaensicke
发布于 2022-01-04
已采纳
0 人赞同

你能确保上传目录在你的Docker容器中存在吗?如果它不存在,Multer就不会创建它。它可能在你的 storage 功能和实际写入文件到磁盘之间默默地失败。

const storage = multer.diskStorage({
  // potential error, path to store files, callback
destination: (req, file, cb) => {
    // cb acceptes two arguments: 1. err 2. destination folder wrt to server.js
cb(null, "uploads/");

应该是这样的。

import { access, constants } from "fs";
import { join } from "path";
const storage = multer.diskStorage({
  // potential error, path to store files, callback
destination: (req, file, cb) => {
    // cb acceptes two arguments: 1. err 2. destination folder wrt to server.js
const currentDirectory: string = process.cwd();
    const uploadDirectory: string = join(currentDirectory, 'uploads/');
    // can we write to this path?
access(uploadDirectory, constants.W_OK, (err) => {
      if (err) {
        console.error(err);
        cb(err, null);