使用http.request获取node.js中的二进制内容

时间:2013-07-24 14:09:07

标签: node.js http binary

我想从https请求中检索二进制数据。

我找到了一个使用请求方法的类似问题Getting binary content in Node.js using request,是说将编码设置为 null 应该有效,但事实并非如此。

options = {
    hostname: urloptions.hostname,
    path: urloptions.path,
    method: 'GET',
    rejectUnauthorized: false,
    encoding: null
};

req = https.request(options, function(res) {
    var data;
    data = "";
    res.on('data', function(chunk) {
        return data += chunk;
    });
    res.on('end', function() {
        return loadFile(data);
    });
    res.on('error', function(err) {
        console.log("Error during HTTP request");
        console.log(err.message);
    });
})

修改:将编码设置为'二进制'无效

7 个答案:

答案 0 :(得分:55)

接受的答案对我不起作用(即将编码设置为二进制),即使是提出问题的用户也无效。

这对我有用,取自:http://chad.pantherdev.com/node-js-binary-http-streams/

http.get(url.parse('http://myserver.com:9999/package'), function(res) {
    var data = [];

    res.on('data', function(chunk) {
        data.push(chunk);
    }).on('end', function() {
        //at this point data is an array of Buffers
        //so Buffer.concat() can make us a new Buffer
        //of all of them together
        var buffer = Buffer.concat(data);
        console.log(buffer.toString('base64'));
    });
});

修改:按照Semicolon的建议更新答案

答案 1 :(得分:15)

您需要将编码设置为响应,而不是请求:

req = https.request(options, function(res) {
    res.setEncoding('binary');

    var data = [ ];

    res.on('data', function(chunk) {
        data.push(chunk);
    });
    res.on('end', function() {
        var binary = Buffer.concat(data);
        // binary is your data
    });
    res.on('error', function(err) {
        console.log("Error during HTTP request");
        console.log(err.message);
    });
});

这是一个有用的答案:Writing image to local server

答案 2 :(得分:8)

在AWS Lambda环境中的NodeJS 6.10(以及2019年2月测试的8.10)上运行,对我来说,没有任何解决方案。

对我有用的是以下内容:

https.get(opt, (res) => {
    res.setEncoding('binary');
    let chunks = [];

    res.on('data', (chunk) => {
        chunks.push(Buffer.from(chunk, 'binary'));
    });

    res.on('end', () => {
        let binary = Buffer.concat(chunks);
        // binary is now a Buffer that can be used as Uint8Array or as
        // any other TypedArray for data processing in NodeJS or 
        // passed on via the Buffer to something else.
    });
});

记下res.setEncoding(' binary');和Buffer.from(chunk,' binary')行。一个设置响应编码,另一个从前面指定的编码中提供的字符串创建一个Buffer对象。

答案 3 :(得分:2)

这里的每个人都在正确的轨道上,但是要解决这个问题,您不能致电.setEncoding()永远。

如果您呼叫.setEncoding(),它将创建一个StringDecoder并将其设置为default decoder。如果尝试传递nullundefined,则它仍将创建一个StringDecoder,其默认解码器为UTF-8。即使您呼叫.setEncoding('binary'),也与呼叫.setEncoding('latin1')相同。是的,seriously

我希望我可以说您将._readableState.encoding_readableState.decoder设置回null,但是当您调用.setEncoding()时,缓冲区被擦除并替换为解码后的二进制编码一连串的东西。这意味着您的数据已被更改。

如果要“撤消”解码,则必须将数据流重新编码回二进制,如下所示:

  req.on('data', (chunk) => {
      let buffer;
      if (typeof chunk === 'string') {
        buffer = Buffer.from(chunk, req.readableEncoding);
      } else {
        buffer = chunk;
      }
      // Handle chunk
  });

当然,如果您从不调用.setEncoding(),则不必担心将块作为string返回。


将块命名为Buffer之后,就可以根据需要使用它了。为了全面,这里介绍了如何使用预设的缓冲区大小,同时还要检查Content-Length

const BUFFER_SIZE = 4096;

/**
 * @param {IncomingMessage} req
 * @return {Promise<Buffer>}
 */
function readEntireRequest(req) {
  return new Promise((resolve, reject) => {
    const expectedSize = parseInt(req.headers['content-length'], 10) || null;
    let data = Buffer.alloc(Math.min(BUFFER_SIZE, expectedSize || BUFFER_SIZE));
    let bytesWritten = 0;
    req.on('data', (chunk) => {
      if ((chunk.length + bytesWritten) > data.length) {
        // Buffer is too small. Double it.
        let newLength = data.length * 2;
        while (newLength < chunk.length + data.length) {
          newLength *= 2;
        }
        const newBuffer = Buffer.alloc(newLength);
        data.copy(newBuffer);
        data = newBuffer;
      }
      bytesWritten += chunk.copy(data, bytesWritten);
      if (bytesWritten === expectedSize) {
        // If we trust Content-Length, we could return immediately here.
      }
    });
    req.on('end', () => {
      if (data.length > bytesWritten) {
        // Return a slice of the original buffer
        data = data.subarray(0, bytesWritten);
      }
      resolve(data);
    });
    req.on('error', (err) => {
      reject(err);
    });
  });
}

此处使用缓冲区大小的选择是避免立即保留大量内存,而是仅根据需要获取RAM。 Promise功能只是为了方便。

答案 4 :(得分:1)

约翰·约翰逊(PärtJohanson),我希望我能发表评论,只为了感谢您将我从递归循环中解救出来,我整天都在梳理头发,然后一遍又一遍地阅读(极其无用的)节点文档。找到您的答案后,我去研究了文档,甚至找不到任何地方记录的res.setEncoding方法!它只是作为两个示例的一部分显示的,其中两个示例称为res.setEncoding('utf8');,您是在哪里找到的,或者是如何找到的呢!?

由于我没有足够的声誉来发表评论,所以我至少会对我的回答做出一些贡献:PärtJohanson的回答对我有100%的作用,我只是根据自己的需要对它进行了一些调整,因为我正在使用它在NWJS 0.36.4 /节点11.11.0上使用nw.Window.get().evalNWBin()下载并评估服务器上托管的脚本(并使用nwjc编译)的脚本:

let opt = {...};
let req = require('https').request(opt, (res) => {
  // server error returned
  if (200 !== res.statusCode) {
    res.setEncoding('utf8');
    let data = '';
    res.on('data', (strData) => {
      data += strData;
    });
    res.on('end', () => {
      if (!res.complete) {
        console.log('Server error, incomplete response: ' + data);
      } else {
        console.log('Server error, response: ' + data);
      }
    });
  }
  // expected response
  else {
    res.setEncoding('binary');
    let data = [];
    res.on('data', (binData) => {
      data.push(Buffer.from(binData, 'binary'));
    });
    res.on('end', () => {
      data = Buffer.concat(data);
      if (!res.complete) {
        console.log('Request completed, incomplete response, ' + data.length + ' bytes received);
      } else {
        console.log('Request completed, ' + data.length + ' bytes received');
        nw.Window.get().evalNWBin(null, data);
      }
    });
  }
};

编辑:PS我发布此邮件是为了防止有人想知道如何处理非二进制响应-我的实际代码更深入一点,并检查响应内容类型标头以解析JSON(预期失败,即400、401、403)或HTML(意外失败,例如404或500)

答案 5 :(得分:0)

  1. 不要调用setEncoding()方法,因为by default, no encoding is assigned and stream data will be returned as Buffer objects
  2. Buffer.from()回调方法中调用on.data,将chunk的值转换为Buffer对象。
http.get('my_url', (response) => {
  const chunks = [];
  response.on('data', chunk => chunks.push(Buffer.from(chunk))) // Converte `chunk` to a `Buffer` object.
    .on('end', () => {
      const buffer = Buffer.concat(chunks);
      console.log(buffer.toString('base64'));
    });
});

答案 6 :(得分:0)

和这里的其他人一样,我需要处理来自Node.js HTTP响应(又名http.IncomingMessage)的二进制数据块。

除了PärtJohanson的answer及其变体之外,现有答案中的任何一个都对我的Electron 6项目(在发布时捆绑了Node.js 12.4.0)没有真正的作用。

即使使用该解决方案,这些块仍总是作为response.on('data', ondata)对象(而不是期望的string对象)到达Buffer处理程序。 Buffer.from(chunk, 'binary')带来了额外的转化。无论使用response.setEncoding('binary')还是response.setEncoding(null)显式指定二进制编码,我都会得到字符串。

我设法获取原始Buffer块的唯一方法是response用管道传输到stream.Writable的实例,在该实例中我提供了自定义的write方法

const https = require('https');
const { Writable } = require('stream');

async function getBinaryDataAsync(url) {
  // start HTTP request, get binary response
  const { request, response } = await new Promise((resolve, reject) => {
    const request = https.request(url, { 
      method: 'GET', 
        headers: { 
          'Accept': 'application/pdf', 
          'Accept-Encoding': 'identity'
        }        
      }
    );

    request.on('response', response => 
      resolve({request, response}));
    request.on('error', reject);
    request.end();
  });

  // read the binary response by piping it to stream.Writable
  const buffers = await new Promise((resolve, reject) => {

    response.on('aborted', reject);
    response.on('error', reject);

    const chunks = [];

    const stream = new Writable({
      write: (chunk, encoding, notifyComplete) => {
        try {
          chunks.push(chunk);
          notifyComplete();      
        }
        catch(error) {
          notifyComplete(error);      
        }
      }
    });

    stream.on('error', reject);
    stream.on('finish', () => resolve(chunks));
    response.pipe(stream);
  });

  const buffer = Buffer.concat(buffers);
  return buffer.buffer; // as ArrayBuffer
}

async function main() {
  const arrayBuff = await getBinaryDataAsync('https://download.microsoft.com/download/8/A/4/8A48E46A-C355-4E5C-8417-E6ACD8A207D4/VisualStudioCode-TipsAndTricks-Vol.1.pdf');
  console.log(arrayBuff.byteLength);
};

main().catch(error => console.error(error));

已更新,此行为仅在我们的Web API服务器中体现。因此,response.on('data')实际上可以很好地用于我在上述代码段中使用的示例URL,因此不需要流。尽管这是特定于服务器的,但是这很奇怪,我正在进一步调查。