自定义存储引擎之前访问表单字段"/>
在使用 Multer 的自定义存储引擎之前访问表单字段
我目前正在使用带有自定义存储引擎的 Multer,该引擎将文件上传到请求中指定的位置。我目前正在从查询中获取目的地和其他几个参数,但我更愿意将所有输入数据合并到表单数据中。此目标输入需要一些额外的验证,需要访问
res
和 next
。目前的实现如下:
upload(req, res, next) {
// Ideally this would be `req.body.destination`;
const destination = req.query.destination;
/* Validation ... */
return multer({
storage: new CustomStorage(destination),
}).single('file')(req, res, next);
}
但是,Multer 需要在此验证之前运行,以便首先将表单数据解析为
req.body
。我试图通过在存储引擎之前运行第二个 Multer 实例来解决这个问题——这个实例会忽略文件并将任何文本字段解析为req.body
。实施如下:
multer({
fileFilter: (req, file, cb) => { return cb(null, false); },
}).any();
运行它确实允许我在上传中间件中访问
req.body
中的表单数据,但随后我收到以下错误:
Error: Unexpected end of form
at Multipart._final (/Users/robert/bucket/node_modules/busboy/lib/types/multipart.js:588:17)
at callFinal (node:internal/streams/writable:694:27)
at prefinish (node:internal/streams/writable:723:7)
at finishMaybe (node:internal/streams/writable:733:5)
at Multipart.Writable.end (node:internal/streams/writable:631:5)
at onend (node:internal/streams/readable:693:10)
at processTicksAndRejections (node:internal/process/task_queues:78:11) {
storageErrors: []
}
我在阅读 issue 1144 后尝试降级到 Multer 1.4.3,这样做确实阻止了错误,但导致 API 路由出错并显示
400
状态并且没有错误消息。
如何访问此处的表单数据字段?
回答如下:正如您正确指出的那样,要使
req.body
填充任何内容,Multer 需要先运行。只有在调用 Multer 中间件后,req
流才会通过 Busboy(Multer 使用的多部分/表单数据请求解析器)传输,并且字段和文件会被解析和填充,按照它们出现的顺序,以req.body
.
这意味着,如果您在任何文件之前发送所有必要的字段,您的
CustomStorage._handleFile()
实现将可以访问req.body
,填充字段。
因此,要直接回答您的问题,您可以在
CustomStorage._handleFile()
中实施字段验证 – req.body
将填充字段,但仅限于在任何文件之前收到的字段! 字段/文件的顺序在这里很重要。
这有几个缺点:
- 将您的验证实现与 Multer 的 API 紧密联系在一起;
- 在某些情况下,您可能没有发送/接收任何文件,但仍想验证字段。
- 每次遇到一个文件时都会重复运行验证——如果你有 3 个文件,对 req.body 的验证将运行 3 次。这应该不会对性能产生巨大影响,但它仍然不是很优雅。
Multer 的设计不够“灵活”以允许此类自定义逻辑,并且它不提供单独处理字段事件的功能,仅提供文件事件。
我已经写了
pechkin
来解决这个确切的问题。
返回一个pechkin.parseFormData()
,它在所有字段都被解析时解析,and 如果遇到第一个文件(或请求结束)。Promise
- 承诺包含一个填充的
对象,和一个fields
files
.AsyncIterator/AsyncIterable
- 无需提供存储引擎、文件处理器、事件监听器等
这意味着您可以对
fields
对象执行验证,然后才需要处理文件——文件不会被解析、处理、写入内存、保存在任何地方等,直到您决定这样做。无需将您的功能包装到自定义存储引擎类中 - 文件可作为 Promise
s of stream.Readable
instances.
// ESM
import { parseFormData } from 'pechkin';
// CommonJS
const { parseFormData } = require('pechkin');
// ...
app.post(
'/upload',
fileUploadWithFieldValidation(/* ...optional configs */),
(req, res, next) => {
// `req.body` will now contain validated fields
// and `req.files` will be an AsyncIterator that you can iterate over
// to process files, save them, upload to S3, etc.
// I.e. do whatever your `CustomEngine` implementation did.
}
);
// Express middleware
function fileUploadWithFieldValidation (/* ...optional configs */) {
return async (req, res, next) => {
try {
const { fields, files } = await parseFormData(req, /* ...optional configs */);
// You can VALIDATE fields here, without having to do anything
// related to file handling / processing.
req.body = fields;
req.files = files;
return next();
} catch (err) {
return next(err);
}
}
}
更多推荐
在使用 Multer 的自定义存储引擎之前访问表单字段
发布评论