使用Express获取上传的文件作为流 [英] Get uploaded file as stream using Express

查看:72
本文介绍了使用Express获取上传的文件作为流的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

现在,我正在尝试制作一个API,其功能之一是获取文件并将其上传到另一个网站,有点像通过API上载文件作为某种代理"./p>

我要将文件发送到的网站需要其他步骤才能工作(即在另一个请求中发送目标),并且我想让我的API在一个请求中完成所有操作,而该请求已经具有所需的所有详细信息

我已经完成了对目的地的请求,但是我很难找到如何将用户上传的内容管道"到我发送到网站的请求中. request-promise 在上载文件时将文件流作为输入,因此我希望希望将来自用户的上载内容传送到请求.这样,我不必使用任何磁盘空间来临时保存文件,特别是因为我的磁盘空间非常有限,并且由于我将其发送到的服务器实际上没有文件限制.

截至目前,我正在使用 express-fileupload 临时保存文件,但这最终会耗尽我极小的(194 MB)分配空间.我正在使用 express v4.16.4和Node v12.0.0.对于请求,我使用 request-promise v4.2.4.

这是我声明Express应用程序和 express-fileupload 对象的方式.

  var router = express.Router({caseSensitive:true});app.use(路由器);...router.use(require("express-fileupload")({useTempFiles:是的,tempFileDir:.data/temp"})); 

这就是我用来将文件扔进网站的方法:

 <代码>如果(req.body.id ===未定义|| req.files.file ===未定义)返回新的APIError(400);如果(!Object.keys(JSON.parse(fs.read(.data/master_list_of_possible_values_for_that_variable.json")))).includes(req.body.id))返回新的APIError("400-ID");var session_flag = await(session.executeRequest({方法:"POST",uri:Constants.URL.upload.session,形式: {id:req.body.id}}).then(r => {return r;}).catch(e => {return e;}));如果(session_flag instanceof错误)抛出session_flag;var upload = await(session.executeRequest({方法:"POST",uri:Constants.URL.upload.fileEndpoint,formData:{文件: {值:fs.createReadStream(req.files.file.tempFilePath),选项: {文件名:req.files.file.name,contentType:req.files.file.mimetype}}}}).then(r => {return r;}).catch(e => {return e;}));如果(上传instanceof错误)上载;返回{success:true}; 

解决方案

我建议您采取以下措施:

  1. 更改端点,以便在URL中发送 id ,这样就无需阅读整个正文即可发出第一个请求
  2. 将请求直接发送到第三方端点

  const request = require('request');//流式处理请求更好const rp = require('request-promise');const {一旦} = require('events');//确保没有为此请求启用任何正文解析器router.post('/:id',async(req,res)=> {尝试 {//发出第一个请求等待rp({方法:"POST",uri:Constants.URL.upload.session,形式: {id:req.body.id}})//管道请求const upload = req.pipe(request({方法:"POST",uri:Constants.URL.upload.fileEndpoint,headers:req.headers//发送相同的Content-Type,Length等}))//会抛出错误"等待一次(上传,响应")//检查状态码(如果需要)const success = upload.response.statusCode === 200;返回res.json({成功})} catch(e){水库状态(500).send('错误')}}); 

这样做,您无需将其存储在临时文件中,因为您可以将其直接管道传输到第三方端点.

Right now, I'm trying to make an API, and one of its functions is to take a file and upload it to another website, sort of like uploading a file through the API as some sort of "proxy".

The website I'm sending the file to requires additional steps to work (namely sending the destination in another request) and I wanted to have my API do that all in one request instead, where that request already had all the details needed.

I've gotten the request for the destination done, but I'm having a hard time finding out how to "pipe" the upload of a user into the request that I send to the website. request-promise takes a file stream as an input when uploading files, so I wanted to hopefully pipe the upload coming from the user to the request. That way I don't have to use any disk space to temporarily save the file, especially since I'm very limited in disk space and since the server I'm sending it to virtually has no file limit.

As of now, I'm using express-fileupload to temporarily save the file, but this will eventually blow up my extremely small (194 MB) allocated space. I'm using express v4.16.4 and Node v12.0.0. For the requests, I use request-promise v4.2.4.

Here's how I declared the Express app and the express-fileupload object.

var router = express.Router({ caseSensitive: true });
app.use(router);

...

router.use(require("express-fileupload")({
    useTempFiles : true,
    tempFileDir : ".data/temp"
}));

And here's what I'm using to toss the file into the website:

if (req.body.id === undefined || req.files.file === undefined)
    return new APIError(400);
if (!Object.keys(JSON.parse(fs.read(".data/master_list_of_possible_values_for_that_variable.json"))).includes(req.body.id))
    return new APIError("400-ID");

var session_flag = await(session.executeRequest({
    method: "POST",
    uri: Constants.URL.upload.session,
    form: {
        id: req.body.id
    }
})
    .then(r => {return r;})
    .catch(e => { return e; }));

if (session_flag instanceof Error)
    throw session_flag;

var upload = await(session.executeRequest({
    method: "POST",
    uri: Constants.URL.upload.fileEndpoint,
    formData: {
        file: {
            value: fs.createReadStream(req.files.file.tempFilePath),
            options: {
                filename: req.files.file.name,
                contentType: req.files.file.mimetype
            }
        }
    }
})
    .then(r => {return r;})
    .catch(e => { return e; }));

if (upload instanceof Error)
    throw upload;

return { success: true };

解决方案

What I suggest you do, is the following:

  1. Change your endpoint so you send id in the URL, that way you don't need to read the whole body to issue the first request
  2. Pipe the request directly to the third party endpoint

const request = require('request'); // streaming works better with request
const rp = require('request-promise');
const { once } = require('events');

// Make sure you don't have any body parser enable for this request
router.post('/:id', async(req, res) => {
   try {

        // issue first request
        await rp({
            method: "POST",
            uri: Constants.URL.upload.session,
            form: {
                id: req.body.id
            }
        })
        // pipe request
        const upload = req.pipe(request({
            method: "POST",
            uri: Constants.URL.upload.fileEndpoint,
            headers: req.headers // Send Same Content-Type, Length and so on
        }))

        // will throw on 'error'
        await once(upload, 'response')

        // check status code if you want
        const success = upload.response.statusCode === 200;

        return res.json({ success })

   } catch(e) {
      res.status(500)
        .send('error')
   }
});

Doing this, you don't need to store it in a temporary file since you're piping it directly to the third party endpoint.

这篇关于使用Express获取上传的文件作为流的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆