简体   繁体   English

强大:解析多部分/表单数据请求,然后上传文件

[英]Formidable: Parse multipart/form-data request THEN upload file

I have a problem since yesterday with my node express app.从昨天开始,我的 node express 应用程序出现了问题。

The thing what I want to do is the next one:我想做的是下一个:

Middleware 1, step 1 (parse request): Parse the multipart/form-data in req.body with formidable and do what I want to do with the data (such validation), then go to step 2 if all being good.中间件 1,步骤 1(解析请求):使用 formidable 解析 req.body 中的 multipart/form-data 并执行我想对数据执行的操作(例如验证),如果一切正常,则 go 到步骤 2。

Middleware 2, step 2 (upload file): Upload file with formidable (already being parsed)中间件2,第2步(上传文件):使用formidable上传文件(已解析)

Problems:问题:

  • Multer: Already tested with Multer but impossible, I have to put the middleware 2 before the middleware 1 otherwise the req.body is empty (also tested multer.none()). Multer:已经用 Multer 测试过但不可能,我必须将中间件 2 放在中间件 1 之前,否则 req.body 为空(也测试了 multer.none())。
  • Formidable: Actively testing with formidable but when I parse the request in the Middleware 1, formidable won't do anything in the Middleware 2 (because my req.body is already parsed and he didn't find the file?) Formidable:用 formidable 积极测试,但是当我在中间件 1 中解析请求时,formidable 不会在中间件 2 中做任何事情(因为我的 req.body 已经解析了,他没有找到文件?)

I know there is a filter function but I really want to have two seperated middlewares.我知道有一个过滤器 function 但我真的想要两个独立的中间件。

Express middlewares快递中间件

const formidable = require('formidable');

// parse request
module.exports.middleware1 = async (req, res, next) => {
  const form = formidable({ multiples: true });

  form.parse(req, (err, fields, files) => {
    if (err) {
      next(err);
      return;
    }
    
    // request parsed
    // do what I want to do with the fields (such validation)
    console.log(fields)
    
    next()
  });
}

// upload file
module.exports.middleware2 = async (req, res, next) => {
  const options = {
    uploadDir: 'test',
    keepExtensions: true,
    maxFiles: 1,
    filename: function (name, ext, part, form) {
      return name
    },
    filter: function ({name, originalFilename, mimetype}) {
      return true
    }
  }
  const form = formidable(options);
  form.parse(req, (err, fields, files) => {
    if (err) {
      next(err);
      return;
    }
    
    // file uploaded, go to controller
    
    next()
  });
}

Front app前端应用

publishPost: function () {
  let formData = new FormData()

  const post = {
    filters: {
      ...this.postFilters
    },
    details: {
      ...this.postDetails
    }
  }

  formData.append('post', JSON.stringify(post))
  formData.append('file', this.postMedia.file)

  postService.createOne(formData).then((res) => {
    if (res) {
      postServiceStatus.createOneDone()
    }
  });
}

If anyone has an idea, I'd be grateful!如果有人有想法,我将不胜感激!

I finally found a solution to my problem !我终于找到了解决问题的方法!

I created a global error handler that handle all the errors on my API, and when an error is related to my route where I created the file (in the first middleware), I delete the file with informations passed in req.file.我创建了一个全局错误处理程序来处理我的 API 上的所有错误,当错误与我创建文件的路径相关时(在第一个中间件中),我删除了包含在 req.file 中传递的信息的文件。

const fs = require('fs')
const { DEFAULT_API_PATH } = process.env

/**
 * Handle middlewares errors
 * @param {Object} err error object
 * @param {String} err.type type of error (service affected by the error)
 * @param {String} err.error original error
 * @param {String} err.message returned error
 * @param {Number} err.status error status
 * @returns 
 */
module.exports = async (err, req, res, next) => {
  if (err.error) console.log(err.error) 
  
  // Delete file
  if (req.path === `${DEFAULT_API_PATH}/posts/create` && req.file) {
    if (fs.existsSync(req.file.path)) {
      fs.unlinkSync(req.file.path, function (err) {
        console.log(err)
      }); 
    }
  } 
  
  if (err.type && err.type === 'database') return res.status(err.status || 400).send({ error: err.message || 'Une erreur innatendue s\'est produite avec la base de données. Veuillez réessayer dans un instant.' }) 
  else if (err.type && err.type === 'server') return res.status(err.status || 400).send({ error: err.message || 'Une erreur innatendue s\'est produite avec le serveur. Veuillez réessayer dans un instant.' }) 
  
  return res.status(err.status || 400).send({ error: err.message || 'Une erreur innatendue s\'est produite. Veuillez réessayer dans un instant.' }) 
}

The server can consume an incoming request only once, because the request body is streamed by the client and the HTTP protocol does not allow the server to request a "rewind" from the client.服务器只能使用一次传入请求,因为请求正文由客户端流式传输,并且 HTTP 协议不允许服务器向客户端请求“倒带”。

The idea of multer and formidable is to write the file to the uploadDir as the request is being consumed . multerformidable的想法是在使用请求时将文件写入uploadDir If you do not want to do that in the first middleware, you need to store the file in a Javascript variable (for example, res.locals.uploadedFile ) and write it to the uploadDir yourself in the second middleware.如果您不想在第一个中间件中执行此操作,则需要将文件存储在 Javascript 变量(例如res.locals.uploadedFile )中,然后在第二个中间件中自行将其写入uploadDir But what would be the advantage of that?但是这样做有什么好处呢? Main memory is more expensive than disk space.主要 memory 比磁盘空间更昂贵。

Why do you insist on two separate middlewares?为什么你坚持使用两个独立的中间件?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM