Node.js 如何在中间件中读取流,并且在下一个中间件中仍然可以流化

Node.js 如何在中间件中读取流,并且在下一个中间件中仍然可以流化,node.js,express,stream,middleware,Node.js,Express,Stream,Middleware,我正在使用代理中间件将多部分数据转发到不同的端点。我希望使用前面的中间件从流中获得一些信息,并且仍然可以为下面的代理中间件读取流。是否存在允许我这样做的流模式 function preMiddleware(req, res, next) { req.rawBody = ''; req.on('data', function(chunk) { req.rawBody += chunk; }); req.on('end', () => { next();

我正在使用代理中间件将多部分数据转发到不同的端点。我希望使用前面的中间件从流中获得一些信息,并且仍然可以为下面的代理中间件读取流。是否存在允许我这样做的流模式

function preMiddleware(req, res, next) {
  req.rawBody = '';

  req.on('data', function(chunk) {
    req.rawBody += chunk;
  });

  req.on('end', () => {
    next();
  })
}

function proxyMiddleware(req, res, next) {
  console.log(req.rawBody)
  console.log(req.readable) // false
}

app.use('/cfs', preMiddleware, proxyMiddleware)

在将流式数据发送到外部端点之前,我想访问
名称
值。我认为我需要这样做,因为端点将
fee
解析为最终的url,我希望有一个句柄来进行一些后期处理。我愿意使用其他模式来解决这个问题。

我认为没有任何机制可以在不从流中永久删除数据的情况下窥视流,也没有任何机制可以从流中“未读”数据以将其放回流中

因此,我可以想出一些可能的想法:

  • 从流中读取所需的数据,然后手动将数据发送到最终端点(不使用需要可读流的代理代码)

  • 读取流,如果需要,则获取所需的数据,然后创建一个新的可读流,将读取的数据放入该可读流,并将该可读流传递给代理。确切地说,如何仅将其传递给代理需要仔细查看代理代码。您可能需要创建一个新的
    req
    对象作为新流

  • 创建一个流转换,允许您在创建可馈送到代理的新流的同时读取流(甚至可能修改它)

  • 注册您自己的
    数据
    事件处理程序,然后暂停流(注册数据甚至会自动触发流流动,您还不想让它流动),然后立即调用
    next()
    。我认为这将允许您在代理中间件读取流时“查看”所有数据的副本,因为只有多个
    数据
    事件处理程序,一个用于您的中间件,另一个用于代理中间件。这是一个理论上的想法——我还没有尝试过


  • 我不认为有任何机制可以在不从流中永久删除数据的情况下偷看流,也没有任何机制可以从流中“未读”数据以将其放回流中

    因此,我可以想出一些可能的想法:

  • 从流中读取所需的数据,然后手动将数据发送到最终端点(不使用需要可读流的代理代码)

  • 读取流,如果需要,则获取所需的数据,然后创建一个新的可读流,将读取的数据放入该可读流,并将该可读流传递给代理。确切地说,如何仅将其传递给代理需要仔细查看代理代码。您可能需要创建一个新的
    req
    对象作为新流

  • 创建一个流转换,允许您在创建可馈送到代理的新流的同时读取流(甚至可能修改它)

  • 注册您自己的
    数据
    事件处理程序,然后暂停流(注册数据甚至会自动触发流流动,您还不想让它流动),然后立即调用
    next()
    。我认为这将允许您在代理中间件读取流时“查看”所有数据的副本,因为只有多个
    数据
    事件处理程序,一个用于您的中间件,另一个用于代理中间件。这是一个理论上的想法——我还没有尝试过


  • 你需要能够向两个不同的方向发送一条流,如果你自己尝试的话,这并不容易——幸运的是,我在那天写了一个有用的模块 ,您可以使用它,我将使用它查找您感兴趣的数据

    我假设您的数据将是一个多部分边界:

    const {StringStream} = require('scramjet');
    const {ReReadable} = require("rereadable-stream");
    
    // I will use a single middleware, since express does not allow to pass an altered request object to next()
    app.use('/cfs', (req, res, next) => {
        const buffered = req.pipe(new ReReadable());        // rewind file to 
        let file = '';                                                  
        buffered.pipe(new StringStream)                     // pipe to a StringStream
            .lines('\n')                                    // split request by line
            .filter(x => x.startsWith('Content-Disposition: form-data;'))
                                                            // find form-data lines
            .parse(x => x.split(/;\s*/).reduce((a, y) => {  // split values
                const z = y.split(/:\s*/);                  // split value name from value
                a[z[0]] = JSON.parse(z[1]);                 // assign to accumulator (values are quoted)
                return a;
            }, {}))
            .until(x => x.name === 'fee' && (file = x.filename, 1))
                                                            // run the stream until filename is found
            .run()
            .then(() => uploadFileToProxy(file, buffered.rewind(), res, next))
                                                            // upload the file using your method
    
    });
    

    您可能需要对其进行一些调整,以使其在真实场景中工作。如果您遇到问题或上面的答案中有问题需要解决,请告诉我。

    您需要能够向两个不同的方向发送单个流,如果您自己尝试,这并不容易-幸运的是,我在当天编写了一个有用的模块 ,您可以使用它,我将使用它查找您感兴趣的数据

    我假设您的数据将是一个多部分边界:

    const {StringStream} = require('scramjet');
    const {ReReadable} = require("rereadable-stream");
    
    // I will use a single middleware, since express does not allow to pass an altered request object to next()
    app.use('/cfs', (req, res, next) => {
        const buffered = req.pipe(new ReReadable());        // rewind file to 
        let file = '';                                                  
        buffered.pipe(new StringStream)                     // pipe to a StringStream
            .lines('\n')                                    // split request by line
            .filter(x => x.startsWith('Content-Disposition: form-data;'))
                                                            // find form-data lines
            .parse(x => x.split(/;\s*/).reduce((a, y) => {  // split values
                const z = y.split(/:\s*/);                  // split value name from value
                a[z[0]] = JSON.parse(z[1]);                 // assign to accumulator (values are quoted)
                return a;
            }, {}))
            .until(x => x.name === 'fee' && (file = x.filename, 1))
                                                            // run the stream until filename is found
            .run()
            .then(() => uploadFileToProxy(file, buffered.rewind(), res, next))
                                                            // upload the file using your method
    
    });
    

    您可能需要对其进行一些调整,以使其在真实场景中工作。如果您遇到问题或上述答案中有问题需要解决,请告诉我。

    我认为您无法“未读”流。您可以将数据放在另一个流中,让下游中间件从另一个流访问数据,不过在这一点上,您也可以让下游中间件访问解析后的数据。如果您告诉我们您试图解决的真正问题,这可能会有所帮助,因为可能还有其他方法。在@jfriend00的评论中添加,一个可能的替代方案是@jfriend00,我已经更新了我的帖子。谢谢请评论任何不清楚的地方。我不认为你可以“未读”流。您可以将数据放在另一个流中,让下游中间件从另一个流访问数据,不过在这一点上,您也可以让下游中间件访问解析后的数据。如果您告诉我们您试图解决的真正问题,这可能会有所帮助,因为可能还有其他方法。在@jfriend00的评论中添加,一个可能的替代方案是@jfriend00,我已经更新了我的帖子。谢谢请评论任何不清楚的地方。