为什么我不能在同一条路线上使用 multer 两次? (Nodejs/Express)
Why can't I use multer twice in the same route? (Nodejs/Express)
问题
我正在使用 express 在 Nodejs API 中开发文件上传功能。我试图阻止超过特定大小限制的文件上传到我的 AWS S3 存储桶。
我知道您可以在其配置中使用 multer 的限制 属性 object:
limits: { fileSize: mbToBytes(sizeLimit) }
问题是在抛出 File Too Large 错误之前,文件上传到 S3 的数量达到了限制,例如,如果我的限制是 6mb 和一个 40mb 的文件已上传,只有前 6mb 会上传到我的存储桶,这就是我要防止的。
我试过的
使用 Multer 将文件存储在内存中,并在上传过程中检查文件是否超过限制,如果超过限制,则抛出错误并停止进程。否则,继续并再次调用 multer 将文件上传到 S3。
// ROUTE
router.post(
'/upload-cover',
userController.checkCoverSize,
userController.uploadCover,
userController.updateUserCover
);
// CONTROLLER
const {
s3CoverMulterConfig,
memoryProfileImgMulterConfig
} = require('../utils/awsMulterConfig');
// Upload cover picture to S3
const coverUploader = new Uploader(s3CoverMulterConfig, 'cover');
exports.uploadCover = coverUploader.startUpload.bind(coverUploader);
// Upload cover picture to memory
const coverMemoryUploader = new Uploader(memoryProfileImgMulterConfig, 'cover');
exports.checkCoverSize = coverMemoryUploader.startUpload.bind(
coverMemoryUploader
);
上传器是 class 因为我要上传不同类型的图片:
// uploader.js
const util = require('util');
const multer = require('multer');
const AppError = require('./appError');
class Uploader {
constructor(options, fileName) {
this.options = options;
this.fileName = fileName;
this.upload = multer(options);
}
async startUpload(req, res, next) {
try {
const upload = util.promisify(this.upload.single(this.fileName));
await upload(req, res);
} catch (err) {
return next(new AppError(err));
}
return next();
}
}
我还有一个文件可以导出我的 multer 配置 objects:
// awsMulterConfig.js
const aws = require('aws-sdk');
const multerS3 = require('multer-s3');
const { nanoid } = require('nanoid');
const multer = require('multer');
const s3 = new aws.S3();
const mbToBytes = (numberInMb) => numberInMb * 1024 * 1024;
aws.config.update({
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
accessKeyId: process.env.AWS_KEY_ID,
region: process.env.REGION
});
// Validate the file type
const fileFilter = (validMimeTypes, errMsg) => (req, file, cb) => {
if (!validMimeTypes.includes(file.mimetype)) {
cb(new Error(errMsg), false);
}
cb(null, true);
};
const generateMulterConfigS3 = (sizeLimit, folderName) => {
return {
limits: { fileSize: mbToBytes(sizeLimit) },
storage: multerS3({
acl: 'public-read',
s3,
bucket: process.env.BUCKET_NAME,
metadata: (req, file, cb) => {
return cb(null, { fieldName: 'TEST_STATIC' });
},
key: (req, file, cb) => {
const fileName = `${folderName}/${nanoid(15)}.${
file.mimetype.split('/')[1]
}`;
return cb(null, fileName);
},
contentType: multerS3.AUTO_CONTENT_TYPE
})
};
};
const generateMulterConfigMemory = (validMimeTypes, errMsg, sizeLimit) => {
return {
fileFilter: fileFilter(validMimeTypes, errMsg),
limits: { fileSize: mbToBytes(sizeLimit) },
storage: multer.memoryStorage()
};
};
exports.s3CoverMulterConfig = generateMulterConfigS3(2, 'cover');
exports.memoryProfileImgMulterConfig = generateMulterConfigMemory(
['image/jpeg', 'image/png'],
'Your image must be .jpeg/jpg or .png!',
2
);
问题的表现
第一个函数正确运行并将文件放入内存,第二个函数运行但没有将文件上传到 S3,因此它没有更新 req.file。如果我删除第一个,第二个将文件正确上传到 S3,就好像你无法在同一路径中使用 Multer 两次解析多部分表单数据。
// ROUTE
router.post(
'/upload-cover',
userController.checkCoverSize,
userController.uploadCover,
userController.updateUserCover
);
我期待的行为
第二个函数应该将文件上传到S3并重写req.file的内容。
我已经验证 checkImgSize
能够在文件大于限制时抛出错误。
我用来发出请求的客户端
邮递员 v7.34.0。
Content-Type header设置为multipart/form-data; boundary= < 发送请求时计算 >
任何帮助将不胜感激:)
您不能在同一个请求中使用 multer
两次,因为一旦从传入读取流中读取数据,它就会消失,除非您将其放在其他地方(例如文件系统中)。相反,您有多种选择:
您可以让它上传到 S3,直到它超过大小限制,然后停止上传,然后从 S3 中删除该项目。
您可以将文件下载到您的文件系统,当它成功完成并且不超过文件大小限制时,您就可以开始将它上传到 S3。如果超过限制,则必须从文件系统中删除部分下载。
您可以将文件下载到内存,当它成功完成并且不超过文件大小限制时,您就可以开始将它上传到S3。如果超过限制,则必须从内存中删除部分下载。
选项 #2 和 #3 在超过文件大小的情况下将带宽节省到 S3(如果这是一项重要的费用),但选项 #1 可能会更快地正常上传到 S3。
问题
我正在使用 express 在 Nodejs API 中开发文件上传功能。我试图阻止超过特定大小限制的文件上传到我的 AWS S3 存储桶。
我知道您可以在其配置中使用 multer 的限制 属性 object:
limits: { fileSize: mbToBytes(sizeLimit) }
问题是在抛出 File Too Large 错误之前,文件上传到 S3 的数量达到了限制,例如,如果我的限制是 6mb 和一个 40mb 的文件已上传,只有前 6mb 会上传到我的存储桶,这就是我要防止的。
我试过的
使用 Multer 将文件存储在内存中,并在上传过程中检查文件是否超过限制,如果超过限制,则抛出错误并停止进程。否则,继续并再次调用 multer 将文件上传到 S3。
// ROUTE
router.post(
'/upload-cover',
userController.checkCoverSize,
userController.uploadCover,
userController.updateUserCover
);
// CONTROLLER
const {
s3CoverMulterConfig,
memoryProfileImgMulterConfig
} = require('../utils/awsMulterConfig');
// Upload cover picture to S3
const coverUploader = new Uploader(s3CoverMulterConfig, 'cover');
exports.uploadCover = coverUploader.startUpload.bind(coverUploader);
// Upload cover picture to memory
const coverMemoryUploader = new Uploader(memoryProfileImgMulterConfig, 'cover');
exports.checkCoverSize = coverMemoryUploader.startUpload.bind(
coverMemoryUploader
);
上传器是 class 因为我要上传不同类型的图片:
// uploader.js
const util = require('util');
const multer = require('multer');
const AppError = require('./appError');
class Uploader {
constructor(options, fileName) {
this.options = options;
this.fileName = fileName;
this.upload = multer(options);
}
async startUpload(req, res, next) {
try {
const upload = util.promisify(this.upload.single(this.fileName));
await upload(req, res);
} catch (err) {
return next(new AppError(err));
}
return next();
}
}
我还有一个文件可以导出我的 multer 配置 objects:
// awsMulterConfig.js
const aws = require('aws-sdk');
const multerS3 = require('multer-s3');
const { nanoid } = require('nanoid');
const multer = require('multer');
const s3 = new aws.S3();
const mbToBytes = (numberInMb) => numberInMb * 1024 * 1024;
aws.config.update({
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
accessKeyId: process.env.AWS_KEY_ID,
region: process.env.REGION
});
// Validate the file type
const fileFilter = (validMimeTypes, errMsg) => (req, file, cb) => {
if (!validMimeTypes.includes(file.mimetype)) {
cb(new Error(errMsg), false);
}
cb(null, true);
};
const generateMulterConfigS3 = (sizeLimit, folderName) => {
return {
limits: { fileSize: mbToBytes(sizeLimit) },
storage: multerS3({
acl: 'public-read',
s3,
bucket: process.env.BUCKET_NAME,
metadata: (req, file, cb) => {
return cb(null, { fieldName: 'TEST_STATIC' });
},
key: (req, file, cb) => {
const fileName = `${folderName}/${nanoid(15)}.${
file.mimetype.split('/')[1]
}`;
return cb(null, fileName);
},
contentType: multerS3.AUTO_CONTENT_TYPE
})
};
};
const generateMulterConfigMemory = (validMimeTypes, errMsg, sizeLimit) => {
return {
fileFilter: fileFilter(validMimeTypes, errMsg),
limits: { fileSize: mbToBytes(sizeLimit) },
storage: multer.memoryStorage()
};
};
exports.s3CoverMulterConfig = generateMulterConfigS3(2, 'cover');
exports.memoryProfileImgMulterConfig = generateMulterConfigMemory(
['image/jpeg', 'image/png'],
'Your image must be .jpeg/jpg or .png!',
2
);
问题的表现
第一个函数正确运行并将文件放入内存,第二个函数运行但没有将文件上传到 S3,因此它没有更新 req.file。如果我删除第一个,第二个将文件正确上传到 S3,就好像你无法在同一路径中使用 Multer 两次解析多部分表单数据。
// ROUTE
router.post(
'/upload-cover',
userController.checkCoverSize,
userController.uploadCover,
userController.updateUserCover
);
我期待的行为
第二个函数应该将文件上传到S3并重写req.file的内容。
我已经验证 checkImgSize
能够在文件大于限制时抛出错误。
我用来发出请求的客户端
邮递员 v7.34.0。 Content-Type header设置为multipart/form-data; boundary= < 发送请求时计算 >
任何帮助将不胜感激:)
您不能在同一个请求中使用 multer
两次,因为一旦从传入读取流中读取数据,它就会消失,除非您将其放在其他地方(例如文件系统中)。相反,您有多种选择:
您可以让它上传到 S3,直到它超过大小限制,然后停止上传,然后从 S3 中删除该项目。
您可以将文件下载到您的文件系统,当它成功完成并且不超过文件大小限制时,您就可以开始将它上传到 S3。如果超过限制,则必须从文件系统中删除部分下载。
您可以将文件下载到内存,当它成功完成并且不超过文件大小限制时,您就可以开始将它上传到S3。如果超过限制,则必须从内存中删除部分下载。
选项 #2 和 #3 在超过文件大小的情况下将带宽节省到 S3(如果这是一项重要的费用),但选项 #1 可能会更快地正常上传到 S3。