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

Next.js 13部署Vercel后Multer上传图片报ENOENT错误求助

问题根源

Vercel 是无服务器(Serverless)平台,其函数运行环境没有持久化的文件系统,你用 multer.diskStorage 指定的 public/uploads 目录在生产环境根本不存在——就算手动创建,也会因为函数实例的生命周期限制无法稳定访问,这就是报错 ENOENT 的核心原因。

解决方案

改用 multer 的 memoryStorage 将文件存储在内存中,直接读取文件 Buffer 上传至 Cloudinary,完全绕开磁盘存储的问题。

步骤1:修改 Multer 配置,替换磁盘存储为内存存储

同时修正原代码中 fileFilter 的错误写法(原 else 分支未正确传递错误信息):

import multer from "multer";
// 改用内存存储,无需指定磁盘目录
const storage = multer.memoryStorage();

const fileFilter = (req, file, cb) => {
  if (
    file.mimetype === "image/jpeg" ||
    file.mimetype === "image/png" ||
    file.mimetype === "image/jpg"
  ) {
    cb(null, true);
  } else {
    // 正确向回调传递错误信息
    cb(new Error("Unsupported file format. Upload only JPEG/JPG or PNG"), false);
  }
};

const upload = multer({
  storage,
  limits: { fieldSize: 1024 * 1024 },
  fileFilter,
});

export default upload;

步骤2:修改 Cloudinary 上传逻辑,直接处理内存中的文件 Buffer

不再依赖磁盘文件路径,改用 file.buffer 直接上传,同时删除无需再使用的文件删除逻辑:

import cloudinary from 'cloudinary';

export const uploadProductImages = async (req, res, next) => { 
   let product = await Product.findById(req.query.id); 
  
   if (!product) { 
     return next(new ErrorHandler("Product not found.", 404)); 
   } 
  
   // 重写uploader,直接接收文件Buffer和文件名
   const uploader = async (buffer, filename) => {
     return new Promise((resolve, reject) => {
       const stream = cloudinary.v2.uploader.upload_stream(
         { 
           folder: "buyitnow/products",
           // 用文件名生成Cloudinary资源ID(可根据需求调整)
           public_id: filename.split('.')[0] 
         },
         (error, result) => {
           if (error) reject(error);
           else resolve(result.secure_url);
         }
       );
       stream.end(buffer);
     });
   };
  
   const urls = []; 
   const files = req.files; 
  
   for (const file of files) { 
     // 直接传入内存中的文件Buffer和文件名
     const imgUrl = await uploader(file.buffer, file.originalname); 
     urls.push(imgUrl);
     // 内存存储无需删除文件,删除原fs.unlinkSync代码
   } 
  
   product = await Product.findByIdAndUpdate(req.query.id, { 
     images: urls, 
   }); 
  
   res.status(200).json({ 
     data: urls, 
     product, 
   }); 
 };
补充说明
  • 内存存储限制:由于文件存在内存中,需确保 limits.fieldSize 设置合理(你原有的1MB限制没问题),避免内存溢出。
  • Cloudinary 适配:如果你的 uploads 函数是自行封装的,需确保其支持接收 Buffer 类型的文件内容,上述示例基于 Cloudinary 官方 SDK 的 upload_stream 实现。

内容的提问来源于stack exchange,提问作者JACOB KYULE

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 07:15:14