经Nginx代理后Express.js使用Multer上传文件失败问题排查
MERN应用VPS部署后图片上传400 Bad Request问题排查
问题概述
已将MERN(MongoDB、Express.js、React.js、Node.js)应用部署至VPS并通过Nginx提供服务,本地开发环境可正常完成带图片上传的产品添加操作,但生产环境执行相同操作时出现400 Bad Request错误。
PM2错误日志
Image upload error: [Error: ENOENT: no such file or directory, open 'public/uploads/products/image-1710418885258.png'] code: 'ENOENT', syscall: 'open', path: 'public/uploads/products/image-1710418885258.png'
相关配置
Nginx配置
server { listen 80; server_name ; location / { root /var/www/vital/Client; index index.html index.htm; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; try_files $uri $uri/ /index.html; } location /api/v1/ { client_max_body_size 500M; proxy_pass http://(vps-ip-address):3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; } }
Multer配置
const multerStorage = multer.diskStorage({ filename: (req, file, cb) => { cb( null, file.fieldname + "-" + Date.now() + path.extname(file.originalname) ); }, destination: (req, file, cb) => { cb(null, "/public/uploads/products"); }, }); const upload = multer({ storage: multerStorage, }); exports.uploadProductImage = (req, res, next) => { upload.single("image")(req, res, (err) => { if (err) { console.error("Image upload error:", err); return res.status(400).json({ status: "error", message: "Image upload failed", }); } next(); }); }; exports.createProduct = catchAsync(async (req, res) => { const productData = { name: req.body.name, description: req.body.description, category: req.body.category, trending: req.body.trending, subCategory: req.body.subCategory || null, tags: req.body.tags, productImage: req.file.filename, }; const newDoc = await Product.create(productData); setTimeout(() => { res.status(201).json({ status: "success", data: { newDoc, }, }); }, 1000); });
路由配置
app.use("/api/v1/products", productRouter); router .route("/create-products") .post(productController.uploadProductImage, productController.createProduct);
排查与解决方向
修复Multer路径配置:当前
destination使用根目录下的/public/uploads/products,但生产环境项目通常部署在子目录(如/var/www/vital/Server),需改为基于项目根目录的动态绝对路径:const path = require('path'); // ... destination: (req, file, cb) => { const uploadPath = path.join(__dirname, '../public/uploads/products'); cb(null, uploadPath); },注意根据项目目录结构调整
../的层级,确保指向正确的存储目录。确认目录存在并设置权限:
- 手动创建缺失的目录(若不存在):
mkdir -p /var/www/vital/Server/public/uploads/products - 给Node进程(PM2运行用户)赋予目录读写权限:
# 若PM2以www-data用户运行 sudo chown -R www-data:www-data /var/www/vital/Server/public/uploads/products sudo chmod -R 755 /var/www/vital/Server/public/uploads/products
- 手动创建缺失的目录(若不存在):
添加Nginx静态文件访问配置:上传的图片需要通过Nginx直接提供访问,在Nginx server块中添加:
location /uploads/ { root /var/www/vital/Server/public; expires 30d; add_header Cache-Control "public, max-age=2592000"; }配置完成后重启Nginx:
sudo systemctl restart nginx验证路径一致性:在生产环境Node项目中添加临时代码打印实际路径,确认Multer使用的路径与VPS物理路径一致:
const uploadPath = path.join(__dirname, '../public/uploads/products'); console.log('Upload path:', uploadPath);
内容的提问来源于stack exchange,提问作者Abdulhamid Abdirahim
相关产品推荐
相关产品推荐

