图片上传(Node + Express)

在 Express 中处理文件上传,最常见的方案是使用 multer 中间件。它基于 busboy 实现,支持内存存储与磁盘存储,并允许自定义过滤逻辑。

来源整理自:vue3js.cn 面试官系列

multer 中间件

const multer = require('multer');
const path = require('node:path');

// 创建文件保存位置及文件保存后缀名
const multerStorage = multer.diskStorage({
  // 缓存位置
  destination: (req, file, cb) => {
    cb(null, 'public/img/users'); // err, 路径名
  },
  filename: (req, file, cb) => {
    const ext = file.mimetype.split('/')[1];
    cb(null, `user-${req.user.id}-${Date.now()}.${ext}`);
  },
});

// 过滤器:判断是否允许上传。可以在这里检测上传的文件是否是图片
const multerFilter = (req, file, cb) => {
  if (file.mimetype.startsWith('image')) {
    cb(null, true);
  } else {
    cb(new Error('Not an image!'), false);
  }
};

const upload = multer({
  storage: multerStorage,
  fileFilter: multerFilter,
  // dest: 'public/img/users', // 文件保存的位置
});

// 上传图片中间件:单文件字段 photo
exports.uploadUserPhoto = upload.single('photo');

补充

补充:完整的上传链路示例(路由 + 中间件 + 静态资源)。

// routes/userRoutes.js
const express = require('express');
const { uploadUserPhoto } = require('../middlewares/upload');
const router = express.Router();

router.patch('/updateMe',
  uploadUserPhoto,                 // 解析 multipart/form-data
  (req, res, next) => {            // 写入数据库前可对图片做 resize / 压缩
    if (!req.file) return next();
    req.body.photo = req.file.filename;
    next();
  },
  userController.updateMe,
);

// app.js 中需要把 uploads 目录暴露为静态资源
app.use('/img/users', express.static(path.join(__dirname, 'public/img/users')));

实战要点:

  • 一定要在 multer 之前先挂 cookieParser、protect 之类的鉴权中间件,否则无法在 req.user 上拿到 id 用于生成文件名。
  • 大文件上传建议走对象存储(OSS/S3),前端直传后再把 key 写入数据库,可极大降低服务器带宽。
  • 生产环境务必设置 limits: { fileSize: 5 * 1024 * 1024 } 等上限,避免被大文件拖垮。