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

如何用Node.js Multer将两张图片上传至AWS S3不同文件夹?

解决方案

1. 调整Multer配置,支持多字段文件上传

修改Multer配置以支持不同字段的文件上传,同时修复原uploadToBucket的异步逻辑问题(原回调方式无法正确返回上传后的URL):

const multer = require('multer')
const AWS = require('aws-sdk')
const fs = require('fs').promises; // 改用Promise版fs,避免同步阻塞
const s3 = new AWS.S3({
  accessKeyId: process.env.BUCKET_ACCESSKEYID,
  secretAccessKey: process.env.BUCKET_SECRETACCESSKEY,
  endpoint: process.env.BUCKET_ENDPOINT
})

const storage = multer.diskStorage({
  filename: function (req, file, cb) {
    let filename = file.originalname.replace(/ /g, '-')
    // 根据字段名区分文件前缀,便于后续分类
    const prefix = file.fieldname === 'image' ? 'event' : 'certificate'
    cb(null, `${prefix}-${Date.now()}-${filename}`)
  }
})

const upload = multer({ storage })

// 封装为Promise函数,支持await调用
const uploadToBucket = async (fileContent, folderName, fileName, fileType) => {
  const params = {
    Bucket: process.env.BUCKET_NAME,
    Key: `${folderName}/${fileName}`,
    Body: await fs.createReadStream(fileContent),
    ContentType: fileType,
    ACL: 'public-read'
  }

  try {
    const data = await s3.upload(params).promise()
    await fs.unlink(fileContent) // 异步删除本地临时文件
    return data.Location
  } catch (err) {
    console.error('S3上传失败:', err)
    throw err // 抛出错误交由上层处理
  }
}

module.exports = { upload, uploadToBucket }

2. 修改路由,配置多字段文件接收

将路由中的上传中间件替换为upload.fields(),指定允许接收的文件字段:

const { s3Service } = require('../../services')

module.exports = (req, res, next) => {
  const controller = new Controller()
  const router = new express.Router()

  // 接收image(必传)和certificate(可选)两个字段的文件
  router.route('/createEvent').post(
    s3Service.upload.fields([
      { name: 'image', maxCount: 1 },
      { name: 'certificate', maxCount: 1 }
    ]),
    controller.createEvent
  )
}

3. 调整控制器逻辑,处理条件上传

在createEvent方法中根据eventCertified字段的值,判断是否需要上传证书图片,并分别将两张图片上传至不同文件夹:

async createEvent(req, res) {
  try {
    const payload = req.body;
    // 处理主图片(必传)
    const imageFile = req.files?.image?.[0];
    if (!imageFile) {
      return res.status(400).json({ message: 'image字段为必填项' });
    }
    const imageUrl = await s3Service.uploadToBucket(
      imageFile.path,
      'events', // 主图片上传至events文件夹
      imageFile.filename,
      imageFile.mimetype
    );
    payload.image = {
      name: imageFile.filename,
      url: imageUrl,
      type: imageFile.mimetype,
      size: imageFile.size
    };

    // 当eventCertified为true时,处理证书图片上传
    if (payload.eventCertified === 'true' || payload.eventCertified === true) {
      const certFile = req.files?.certificate?.[0];
      if (!certFile) {
        return res.status(400).json({ message: 'eventCertified为true时,certificate字段为必填项' });
      }
      const certUrl = await s3Service.uploadToBucket(
        certFile.path,
        'certificates', // 证书图片上传至certificates文件夹
        certFile.filename,
        certFile.mimetype
      );
      payload.certificate = {
        name: certFile.filename,
        url: certUrl,
        type: certFile.mimetype,
        size: certFile.size
      };
    }

    const data = await Event.create(payload);
    res.status(201).json(data);
  } catch (err) {
    console.error('创建事件失败:', err);
    res.status(500).json({ message: '服务器内部错误' });
  }
}

关键说明

  • 使用upload.fields()而非upload.array(),适配不同字段名的文件上传需求
  • 重构uploadToBucket为Promise异步函数,确保能正确获取S3返回的文件URL,解决原回调中return无效的问题
  • 根据eventCertified的值做条件判断,同时补充必填字段校验,避免异常情况
  • 通过uploadToBucket的folderName参数,分别将主图片和证书图片上传至指定的S3文件夹

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.08 00:05:21