强化:分析多部分/form-data请求然后上传文件

发布于 2025-02-03 03:29:39 字数 1899 浏览 2 评论 0原文

自昨天以来,我的Node Express应用程序有问题。

我想做的是下一个:

中间件1,步骤1(解析请求): 用强大的req.body中的多部分/form-data来解析我想对数据(此类验证)做的事情,然后如果一切都很好,请转到步骤2。

中间件2,步骤2(上传文件): 上传文件具有强大的问题(已经被解析)

问题:

  • multer:已经用Multer进行了测试,但不可能,我必须将中间件2放在中间件1之前,否则Req.body是空的(也已测试了Multer.none())。
  • 但是当我在中间件1中解析请求时,强大的中间件2(因为我的req。

强大:用强大的可实现测试, 一个过滤器功能,但我真的想拥有两个分开的中间。

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

如果有人有了想法,我会很感激!

I have a problem since yesterday with my node express app.

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.

Middleware 2, step 2 (upload file):
Upload file with formidable (already being parsed)

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()).
  • 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 ?)

I know there is a filter function but I really want to have two seperated middlewares.

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

拔了角的鹿 2025-02-10 03:29:39

我终于找到了解决问题的解决方案!

我创建了一个全局错误处理程序,该处理程序可以处理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.' }) 
}

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.

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.' }) 
}
千柳 2025-02-10 03:29:39

服务器只能消耗一次传入的请求,因为客户端是流的,并且HTTP协议不允许服务器从客户端请求“倒带”。

multer强大的想法是将文件写入uploadDir ,因为该请求正在消耗。如果您不想在第一个中间件中执行此操作,则需要将文件存储在JavaScript变量中(例如,res.locals.uploadedfile)并将其写入uploadDir << /代码>您自己在第二个中间件中。但是,这是什么优势?主内存比磁盘空间昂贵。

您为什么要坚持两个单独的中间人?

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.

The idea of multer and formidable is to write the file to the uploadDir as the request is being consumed. 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. But what would be the advantage of that? Main memory is more expensive than disk space.

Why do you insist on two separate middlewares?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文