如何解压(解压缩)NodeJS请求模块的gzip响应体?

85

如何解压请求模块响应中的gzip数据?

我尝试了网上的几个示例,但似乎都不起作用。

request(url, function(err, response, body) {
    if(err) {
        handleError(err)
    } else {
        if(response.headers['content-encoding'] == 'gzip') {    
            // How can I unzip the gzipped string body variable?
            // For instance, this url:
            // http://highsnobiety.com/2012/08/25/norse-projects-fall-2012-lookbook/
            // Throws error:
            // { [Error: incorrect header check] errno: -3, code: 'Z_DATA_ERROR' }
            // Yet, browser displays page fine and debugger shows its gzipped
            // And unzipped by browser fine...
            if(response.headers['content-encoding'] && response.headers['content-encoding'].toLowerCase().indexOf('gzip') > -1) {   
                var body = response.body;                    
                zlib.gunzip(response.body, function(error, data) {
                    if(!error) {
                        response.body = data.toString();
                    } else {
                        console.log('Error unzipping:');
                        console.log(error);
                        response.body = body;
                    }
                });
            }
        }
    }
}

3
浏览器不应该自动完成这个吗? - Shi
我添加了node.js标签,但这并不清楚...让我编辑一下帖子... - izk
你能否将数据保存到文件req.gz并从命令行解压缩它?如果可以,那么gunzip req.gzfile req.gz的输出是什么? - Andrey Sidorov
嗨,安德鲁!感谢您的建议。如果我将文件保存为“req.gz”文件,在桌面上提取它会产生一个名为“req.gz.cpgz”的文件。依次提取此文件会产生第三个名为“req 2.gz”的文件。请求正文在读取正文之前被编码为utf8(response.setEncoding('utf8'))。但是,似乎没有什么区别。我得到了相同的错误和类似的桌面文件结果。 - izk
请求3.0版本将在Node v0.10发布后自动支持此功能。 - Jonathan Ong
11个回答

74

我也无法让请求工作起来,所以最终使用了 HTTP。

var http = require("http"),
    zlib = require("zlib");

function getGzipped(url, callback) {
    // buffer to store the streamed decompression
    var buffer = [];

    http.get(url, function(res) {
        // pipe the response into the gunzip to decompress
        var gunzip = zlib.createGunzip();            
        res.pipe(gunzip);

        gunzip.on('data', function(data) {
            // decompression chunk ready, add it to the buffer
            buffer.push(data.toString())

        }).on("end", function() {
            // response and decompression complete, join the buffer and return
            callback(null, buffer.join("")); 

        }).on("error", function(e) {
            callback(e);
        })
    }).on('error', function(e) {
        callback(e)
    });
}

getGzipped(url, function(err, data) {
   console.log(data);
});

终于了!我一直在尝试设置头部接受gzip,尝试代理和各种东西,但这个方法使我能够使用stackoverflow API了!只有一点小问题:var gunzip = gzip.createGunzip(); 应该改为 var gunzip = zlib.createGunzip(); - mlunoe
我尝试了所有的请求方法,但都失败了。这个可以! - paradite
这个可以运行,但是有一种更好更简单的方法可以通过设置请求模块上的几个选项来实现。请查看我下面的答案。 - Sai Teja

36

尝试在传递给request的选项中添加encoding: null,这将避免将下载的正文转换为字符串并将其保留在二进制缓冲区中。


3
我遇到了同样的问题,这个编码选项对我起作用了。谢谢!! - appanponn

31

像@Iftah所说,设置encoding: null

完整的示例(较少错误处理):

request = require('request');
zlib = require('zlib');

request(url, {encoding: null}, function(err, response, body){
    if(response.headers['content-encoding'] == 'gzip'){
        zlib.gunzip(body, function(err, dezipped) {
            callback(dezipped.toString());
        });
    } else {
        callback(body);
    }
});

30

实际上,请求模块可以处理gzip响应。为了告诉请求模块在回调函数中解码body参数,我们必须在选项中将'gzip'设置为true。让我用一个例子来解释一下。

示例:

var opts = {
  uri: 'some uri which return gzip data',
  gzip: true
}

request(opts, function (err, res, body) {
 // now body and res.body both will contain decoded content.
})

注意:您在“响应”事件中获得的数据未解码。

这对我有用。希望它也对你们有用。

我们在使用请求模块时通常会遇到类似的问题是JSON解析。让我来解释一下。如果你想要请求模块自动解析主体并在body参数中提供JSON内容。那么你必须在选项中将“json”设置为true。

var opts = {
  uri:'some uri that provides json data', 
  json: true
} 
request(opts, function (err, res, body) {
// body and res.body will contain json content
})

参考:https://www.npmjs.com/package/request#requestoptions-callback


谢谢你!它有效了,我不知道request-promise有一个gzip标志。 - Steven R
1
设置"gzip": true选项的一个注意事项是,服务器响应必须包含"Content-Encoding": "gzip",请求模块才能实际解压缩响应。我一直在处理一个没有正确设置"Content-Encoding"头的服务器,直到我阅读了请求模块的源代码才发现这是必需的。希望这个评论能帮助其他人节省时间,如果你面临类似的情况,就不用费力去弄清楚为什么不起作用了。 - dstricks

7

https://gist.github.com/miguelmota/9946206所示:

截至2017年12月,request和request-promise都可以直接处理它:

var request = require('request')
  request(
    { method: 'GET'
    , uri: 'http://www.google.com'
    , gzip: true
    }
  , function (error, response, body) {
      // body is the decompressed response body
      console.log('server encoded the data as: ' + (response.headers['content-encoding'] || 'identity'))
      console.log('the decoded data is: ' + body)
    }
  )

5

在尝试不同的gunzip方式并解决与编码相关的错误后,我制定了一个更完整的答案。希望这对你也有所帮助:(完整答案)

var request = require('request');
var zlib = require('zlib');

var options = {
  url: 'http://some.endpoint.com/api/',
  headers: {
    'X-some-headers'  : 'Some headers',
    'Accept-Encoding' : 'gzip, deflate',
  },
  encoding: null
};

request.get(options, function (error, response, body) {

  if (!error && response.statusCode == 200) {
    // If response is gzip, unzip first
    var encoding = response.headers['content-encoding']
    if (encoding && encoding.indexOf('gzip') >= 0) {
      zlib.gunzip(body, function(err, dezipped) {
        var json_string = dezipped.toString('utf-8');
        var json = JSON.parse(json_string);
        // Process the json..
      });
    } else {
      // Response is not gzipped
    }
  }

});

4

以下是我的建议。我曾遇到同样的问题,后来找到了一个很棒的库,叫做concat-stream

let request = require('request');
const zlib = require('zlib');
const concat = require('concat-stream');

request(url)
  .pipe(zlib.createGunzip())
  .pipe(concat(stringBuffer => {
    console.log(stringBuffer.toString());
  }));

1
当远程文件实际上是一个预压缩的.gz文件时,这是我唯一有效的方法。 - Ecker00

3

我正在使用 node-fetch。我获取到了 response.body,但我真正想要的是 await response.text()


1
在我的情况下,我使用了response.json(),所以出现了一些错误,但是在使用response.text()之后它就正常工作了。非常感谢 :) - Ganesh Thorat

3
这里有一个工作示例(使用Node的请求模块),用于解压缩响应。
function gunzipJSON(response){

    var gunzip = zlib.createGunzip();
    var json = "";

    gunzip.on('data', function(data){
        json += data.toString();
    });

    gunzip.on('end', function(){
        parseJSON(json);
    });

    response.pipe(gunzip);
}

完整代码:https://gist.github.com/0xPr0xy/5002984


非常感谢。我在处理这个问题时遇到了困难,使用了您的解决方案,现在完美地解决了。 - thtsigma

2
使用 got,一个 request 的替代方案,您可以简单地执行以下操作:
got(url).then(response => {
    console.log(response.body);
});

需要时,自动处理解压缩。

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