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

