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
相关产品推荐
相关产品推荐

