如何在Node中限制简化HTTP请求响应的内容长度?

5
我想设置简化的HTTP请求()客户端包,以中止下载过大的HTTP资源。假设已经设置了request()来下载一个URL,而资源大小为5GB。我希望request()在下载10MB后停止下载。通常,当request()得到一个答案时,它会得到所有的HTTP头和所有内容。一旦您处理数据,您已经拥有了所有已下载的数据。在axios中,有一个名为maxContentLength的参数,但我找不到类似于request()的任何内容。我还必须提到,我不希望它捕获错误,而只是下载至少标题和资源的开头。

1
这个有帮助吗?-- https://dev59.com/gWUo5IYBdhLWcg3w9jV4 - Jackthomson
谢谢,但我使用一个叫做 request 的包,它是 nodejs HTTP 请求的简化版本。尽管如此,你的回答可能会有用,因为如果我得不到答案,我将不得不用来自 node 的 HTTP 请求替换 request 包。 - Nicolas Guérinet
1
你是在说这个包 - https://www.npmjs.com/package/request 吗?如果是这样,你不能像上面链接的答案那样使用它吗?这个请求库仍然返回相同的响应对象,所以它将具有原型,并且你可以像以前一样钩入它,对吧?或者这个库删除了这个功能?这个库只是通过一些漂亮的语法使过程变得更容易。在其核心,它只是返回一个节点请求和响应对象。 - Jackthomson
1
是的,我说的是npmjs.com/package/request。你给了我一个想法。这个包中有一种方法叫做.pipe,可以捕获传入字节流。我知道它是因为在原始的HTTP请求中就是这样工作的。那么,我应该能够复制这个逻辑。通常情况下,我应该能够在这个方法中计算字节数,并在有太多字节时中止请求。 - Nicolas Guérinet
不错,听起来像是个好主意!如果行得通的话,请告诉我! - Jackthomson
@NicolasGuérinet 你好啊,问题解决了吗?如果是的话,请不要忘记标记答案为已接受。 :) - mehari
3个回答

4
const request = require('request');
const URL = 'http://de.releases.ubuntu.com/xenial/ubuntu-16.04.3-desktop-amd64.iso';
const MAX_SIZE = 10 * 1024 * 1024 // 10MB , maximum size to download
let total_bytes_read = 0;

1 - 如果服务器响应是gzip压缩的,您应该启用gzip选项。     https://github.com/request/request#examples为了向后兼容,默认情况下不支持响应压缩。     要接受gzip压缩的响应,请将gzip选项设置为true。

request
    .get({
        uri: URL,
        gzip: true
    })
    .on('error', function (error) {
        //TODO: error handling
        console.error('ERROR::', error);
    })
    .on('data', function (data) {
        // decompressed data 
        console.log('Decompressed  chunck Recived:' + data.length, ': Total downloaded:', total_bytes_read)
        total_bytes_read += data.length;
        if (total_bytes_read >= MAX_SIZE) {
            //TODO: handle exceeds max size event
            console.error("Request exceeds max size.");
            throw new Error('Request exceeds max size'); //stop
        }
    })
    .on('response', function (response) {
        response.on('data', function (chunk) {
            //compressed data
            console.log('Compressed  chunck Recived:' + chunk.length, ': Total downloaded:', total_bytes_read)
        });
    })
    .on('end', function () {
        console.log('Request completed! Total size downloaded:', total_bytes_read)
    });

注意: 如果服务器没有压缩响应,但您仍然使用gzip选项/解压缩,则解压缩块和原始块将相等。因此,您可以从解压缩的/压缩的块中进行限制检查。但是,如果响应已经压缩,您应该检查解压缩块的大小限制。

2-如果响应未经过压缩,则无需使用gzip选项进行解压缩。

request
    .get(URL)
    .on('error', function (error) {
        //TODO: error handling
        console.error('ERROR::', error);
    })
    .on('response', function (response) {
        response.on('data', function (chunk) {
            //compressed data
            console.log('Recived chunck:' + chunk.length, ': Total downloaded:', total_bytes_read)
            total_bytes_read += chunk.length;
            if (total_bytes_read >= MAX_SIZE) {
                //TODO: handle exceeds max size event
                console.error("Request as it exceds max size:")
                throw new Error('Request as it exceds max size');
            }
            console.log("...");
        });
    })
    .on('end', function () {
        console.log('Request completed! Total size downloaded:', total_bytes_read)
    });

2
你可以在使用request包时,也可以使用data事件。我已经进行了测试,并且对我来说效果很好。
var request = require("request");

var size = 0;
const MAX_SIZE = 200;
request
    .get('http://google.com/')
    .on('data', function(buffer){
        // decompressed data as it is received

        size += buffer.length;

        if (size > MAX_SIZE) {
            console.log("Aborting this request as it exceeds max size")
            this.abort();
        }
        console.log("data coming");

    }).on('end', function() {
        console.log('ending request')
    })
    .on('response', function (response) {
        console.log(response.statusCode) // 200
        console.log(response.headers['content-type']) // 'image/png'
        response.on('data', function (data) {
            // compressed data as it is received
            console.log('received ' + data.length + ' bytes of compressed data')
            // you can size and abort here also if you want.
        })
    });

你可以在获取压缩数据或获取未压缩数据的两个地方进行大小检查(基于https://www.npmjs.com/package/request给出的示例)。


1
如@Jackthomson在第一个评论的答案中指出,可以使用.on(data)来实现。如果您想要头部信息,可以从响应中获取它们,还可以检查content-length头部并且不开始分块传输。这是axios参考文献中关于maxContentLength的定义://maxContentLength定义了允许的http响应内容的最大大小maxContentLength:2000。这就是axios处理maxContentLength的方式。
var responseBuffer = [];
        stream.on('data', function handleStreamData(chunk) {
          responseBuffer.push(chunk);

          // make sure the content length is not over the maxContentLength if specified
          if (config.maxContentLength > -1 && Buffer.concat(responseBuffer).length > config.maxContentLength) {
            reject(createError('maxContentLength size of ' + config.maxContentLength + ' exceeded',
              config, null, lastRequest));
          }
        });

部分 request 等价

var request = require("request");

const MAX_CONTENT_LENGTH = 10000000;

var receivedLength = 0;

var req = request.get('http://de.releases.ubuntu.com/xenial/ubuntu-16.04.3-desktop-amd64.iso')
    .on('response', (response) => {
        if (response.headers['content-length'] && response.headers['content-length'] > MAX_CONTENT_LENGTH) {
            console.log("max content-length exceeded")
            req.abort();
        }
    })
    .on('data', (str) => {
        receivedLength += str.length;
        if (receivedLength > MAX_CONTENT_LENGTH) {
            console.log("max content-length exceeded")
            req.abort();
        }
    })

content-length 头部是文件的总大小,而不是单个块的大小。OP 希望在达到最大值后停止下载。 - mehari
我同意。OP还提到了axios和maxContentLength。编辑答案并添加了axios的参考资料。 - Stamos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接