如何使用Multer S3将多文件同时上传至多个S3存储桶?
我见过很多关于使用Multer上传多文件的问题,我本地已经实现了该功能,现在我的疑问是*是否可以使用Multer将多文件上传到不同的存储中?*目前我将文件上传到S3的一个存储桶,现在我希望在同一次请求中,将相同的文件同时上传到S3的bucket#1和bucket#2两个存储桶。
以下是我当前使用Multer S3实现多文件上传的代码:
const uploadsBusinessGallery = multer({ storage: multerS3({ s3: s3.s3_BUCKET_TEST, bucket: process.env.AWS_BUCKET_BUCKET_TEST, acl: "public-read", contentType: multerS3.AUTO_CONTENT_TYPE, key: function (req, file, cb) { const pathForder = req.url.split("/"); var newFileName = path.basename(file.originalname, path.extname(file.originalname)) + path.extname(file.originalname); var fullPath = pathForder[pathForder.length - 1].replace("%20", " ") + "/" + newFileName; console.log(fullPath); cb(null, fullPath); }, }), /* limits: { fileSize: 2000000 }, // In bytes: 2000000 bytes = 2 MB */ fileFilter: function (req, file, cb) { checkFileType(file, cb); }, }).array("galleryImage", 500);
如您所见,当前我将文件上传到名为AWS_BUCKET_BUCKET_TEST的存储桶,我想要将相同的文件上传到S3的第二个存储桶。
我尝试了先将文件上传到AWS_BUCKET_BUCKET_TEST(bucket#1)后,复用同一份请求上传到第二个存储桶:
我的思路是:当Multer将文件上传到bucket#1后,将相同的请求传给另一个Multer实例,上传到第二个存储桶
function uploadFiles(req, res) { uploadsBusinessGallery(req, res, (error) => { if (error) { console.log("errors en la integración", error); return; } else { // If File not found if (req.files === undefined) { console.log("Error: No File Selected!"); } else { // If Success integration(req,res); } } });
const integration = (req, res) => { uploadsFilesIntegration(req, res, (error) => { console.log("*********************************"); console.log("Backend integration"); if (error) { console.log("errors en la integración", error); return; } else { // If File not found if (req.files === undefined) { console.log("Error: No File Selected!"); } else { // If Success console.log('Archivos integrados') } } }); };
负责上传文件到名为AWS_BUCKET_CD的bucket#2的Multer配置如下:
const uploadsFilesIntegration = multer({ storage: multerS3({ s3: s3.s3_CD, bucket: process.env.AWS_BUCKET_CD, acl: "public-read", contentType: multerS3.AUTO_CONTENT_TYPE, key: function (req, file, cb) { const pathForder = "folder1/subfolder2/subfolder3"; console.log('s3.s3_CD',s3.s3_CD) var newFileName = path.basename(file.originalname, path.extname(file.originalname)) + path.extname(file.originalname); var fullPath = pathForder + "/" + newFileName; console.log(fullPath); cb(null, fullPath); }, }), /* limits: { fileSize: 2000000 }, // In bytes: 2000000 bytes = 2 MB */ fileFilter: function (req, file, cb) { checkFileType(file, cb); }, }).array("galleryImage", 500);
但是我发现,当Multer完成bucket#1的文件上传后,会修改请求对象,导致第二个Multer实例无法读取到文件,无法完成bucket#2的上传。
我尝试过克隆请求对象传给不同的Multer实例但没有成功,也尝试过为单个Multer配置多个存储,但都没有找到可行的实现方案。
恳请各位帮忙提供解决方案,非常感谢。
备注:两个Multer实例使用相同的fieldName,因为我需要将相同的文件上传到两个存储桶。
你遇到的问题核心原因是:HTTP 请求的文件流是一次性的,第一个 Multer 实例处理上传时已经将流消费完毕,第二个实例自然读取不到任何文件内容,复用或浅克隆请求都无法解决这个问题。以下是两种可行的实现方案:
方案一:自定义 Multer 存储,单次请求同时上传两个桶(推荐)
直接实现自定义的 Multer 存储逻辑,在文件流处理阶段同时往两个 S3 桶上传内容,不需要重复处理请求流,性能最优。
const multer = require('multer'); const multerS3 = require('multer-s3'); // 提前初始化好你的两个S3实例:s3.s3_BUCKET_TEST、s3.s3_CD // 自定义双桶存储逻辑 const dualS3Storage = { _handleFile: function (req, file, cb) { // 并行执行两个桶的上传任务 const uploadBucket1 = new Promise((resolve, reject) => { multerS3({ s3: s3.s3_BUCKET_TEST, bucket: process.env.AWS_BUCKET_BUCKET_TEST, acl: "public-read", contentType: multerS3.AUTO_CONTENT_TYPE, key: function (req, file, cb) { const pathForder = req.url.split("/"); const newFileName = path.basename(file.originalname, path.extname(file.originalname)) + path.extname(file.originalname); const fullPath = pathForder[pathForder.length - 1].replace("%20", " ") + "/" + newFileName; cb(null, fullPath); } })._handleFile(req, file, (err, info) => err ? reject(err) : resolve(info)) }) const uploadBucket2 = new Promise((resolve, reject) => { multerS3({ s3: s3.s3_CD, bucket: process.env.AWS_BUCKET_CD, acl: "public-read", contentType: multerS3.AUTO_CONTENT_TYPE, key: function (req, file, cb) { const pathForder = "folder1/subfolder2/subfolder3"; const newFileName = path.basename(file.originalname, path.extname(file.originalname)) + path.extname(file.originalname); const fullPath = pathForder + "/" + newFileName; cb(null, fullPath); } })._handleFile(req, file, (err, info) => err ? reject(err) : resolve(info)) }) // 等待两个桶都上传完成后回调 Promise.all([uploadBucket1, uploadBucket2]) .then(([res1, res2]) => cb(null, {bucket1: res1, bucket2: res2})) .catch(err => cb(err)) }, _removeFile: function (req, file, cb) { // 可选实现:上传出错时删除已上传的文件,避免脏数据 cb(null) } } // 初始化Multer实例,使用自定义双桶存储 const uploadToDualBuckets = multer({ storage: dualS3Storage, fileFilter: (req, file, cb) => checkFileType(file, cb) }).array("galleryImage", 500)
使用时直接调用uploadToDualBuckets中间件处理请求即可,req.files中会同时保留两个存储桶的上传结果。
方案二:先缓存文件再分别上传(适合小文件场景)
如果不想自定义存储,也可以先将文件缓存到服务器内存/本地临时目录,再调用AWS S3 SDK分别上传到两个桶:
// 配置内存存储,先把文件读到内存中 const memoryUpload = multer({ storage: multer.memoryStorage(), fileFilter: (req, file, cb) => checkFileType(file, cb) }).array("galleryImage", 500) async function uploadFiles(req, res) { memoryUpload(req, res, async (err) => { if (err) return res.status(500).send('文件读取失败:' + err.message) if (!req.files || req.files.length === 0) return res.status(400).send('未选择上传文件') try { // 遍历所有文件,并行上传到两个桶 const uploadTasks = req.files.map(file => { // 上传到bucket1 const task1 = s3.s3_BUCKET_TEST.upload({ Bucket: process.env.AWS_BUCKET_BUCKET_TEST, Key: `${req.url.split("/").pop().replace("%20", " ")}/${file.originalname}`, Body: file.buffer, ACL: 'public-read', ContentType: file.mimetype }).promise() // 上传到bucket2 const task2 = s3.s3_CD.upload({ Bucket: process.env.AWS_BUCKET_CD, Key: `folder1/subfolder2/subfolder3/${file.originalname}`, Body: file.buffer, ACL: 'public-read', ContentType: file.mimetype }).promise() return Promise.all([task1, task2]) }) await Promise.all(uploadTasks) res.status(200).send('所有文件已上传到两个存储桶') } catch (uploadErr) { res.status(500).send('上传失败:' + uploadErr.message) } }) }
如果上传的文件体积较大,可以将内存存储换成multer.diskStorage缓存到本地磁盘,所有上传完成后再删除本地临时文件即可。
内容的提问来源于stack exchange,提问作者Victor Potes

