NodeJS 使用Multer在自定义存储引擎之前访问表单字段

sbtkgmzw  于 2023-05-22  发布在  Node.js
关注(0)|答案(1)|浏览(225)

我目前正在使用Multer和一个自定义存储引擎,该引擎可以将文件上传到请求中指定的位置。我目前正在从查询中获取目的地和其他几个参数,但我更喜欢将所有输入数据合并到表单数据中。这个目标输入需要一些额外的验证,需要访问resnext。当前的实现如下:

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,没有错误消息。
如何访问此处的表单数据字段?

kyvafyod

kyvafyod1#

正如您正确指出的那样,要使用任何内容填充req.body,需要首先运行Multer。只有当您调用Multer中间件时,req流才会通过Busboy(Multer使用的多部分/表单数据请求解析器),字段和文件会被解析并填充,按照它们出现的顺序,到req.body
这意味着,如果您在任何文件之前发送所有必要的字段,您的CustomStorage._handleFile()实现将可以访问req.body,并填充字段。
因此,为了直接回答您的问题,您可以在CustomStorage._handleFile()中实现字段验证-req.body将填充字段,**但只有那些在任何文件之前收到的字段!**这里字段/文件的顺序很重要。
这有几个缺点:
1.这将您的验证实现与Multer的API紧密联系在一起;
1.有时候,您可能没有发送/接收任何文件,但仍希望验证字段。
1.每次遇到一个文件时,验证将冗余地运行-如果您有3个文件,则对req.body的验证将运行三次。这应该不会对性能有很大的影响,但仍然不是很优雅。
Multer的设计不够灵活,不允许这样的定制逻辑,它不提供单独处理字段事件的功能,只提供文件事件。
我写了pechkin来解决这个问题。

  • pechkin.parseFormData()返回一个Promise,当所有字段都被解析时,如果遇到第一个文件(或请求结束)。
  • promise包含一个已填充的fields对象和一个filesAsyncIterator/AsyncIterable
  • 无需提供存储引擎、文件处理程序、事件侦听器等

这意味着您可以对fields对象执行验证,然后才必须处理文件-文件不会被解析,处理,写入内存,保存在任何地方,直到您决定这样做。无需将您的功能 Package 到自定义存储引擎类中-文件可以作为Promisestream.Readable示例提供。

// 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);
    }
  }
}

相关问题