使用 JavaScript 和 Node.js 循环检索 pdf 发票(Meteor)

Recurly pdf invoice retrieving with JavaScript and Node.js (Meteor)

我们正在开发一个具有 Recurly 集成的应用程序,并尝试使用它的 PDF 发票功能。

该应用程序基于Node.js(Meteor 平台)。

它使用二进制文件从 Recurly 接收到正确答案:

但是我无法正确保存它。我尝试了两种方法:在浏览器的客户端打印它:

var file = window.URL.createObjectURL(new Blob([r.content], {type: "application/pdf"}));
  var a = document.createElement("a");
  a.href = file;
  a.download = "invoicePDF";
  document.body.appendChild(a);
  a.click();

  window.onfocus = function () {
   document.body.removeChild(a)
  }

并直接保存在服务器上(仅供测试):

var fs = require('fs');
var wstream = fs.createWriteStream('C:/recurly.pdf');
wstream.write(result.content);
wstream.end();

但在这两种情况下,我都得到了无法使用的 pdf 文件。 Acrobat、Foxit reader 和 Chrome 无法打开此文件 — 它已损坏。

你对我错的地方有什么建议吗?也许我需要在保存之前进行一些内容转换或其他任何事情?

已添加

我已将此请求的结果发送给客户端并打印在控制台中(上图)。

try {
      result = HTTP.call(
        'GET',
        'https://' + Meteor.settings.recurly.SUBDOMAIN + '.recurly.com/v2/invoices/' + invoiceId,
        {
          headers: {
            Authorization: "Basic " + (new Buffer(Meteor.settings.recurly.API_KEY)).toString('base64'),
            Accept: 'application/pdf'
          }
        }
      );
    } catch (err) {
      result = e;
    }

问题是您正在尝试获取一个二进制文件,该文件包含一个期望编码字符串作为响应的请求。

你最好的选择是告诉请求库它应该期待什么,否则你将不得不手动从 UTF-16- 或 UTF-8- 编码的字符串中撬出二进制数据。

客户端和服务器实现之间存在差异。


服务器

服务器实现使用节点的 request module. You can supply options for it 使用 npmRequestOptions

如其文档中所述:

encoding - Encoding to be used on setEncoding of response data. If null, the body is returned as a Buffer. Anything else (including the default value of undefined) will be passed as the encoding parameter to toString() (meaning this is effectively utf8 by default). (Note: if you expect binary data, you should set encoding: null.)

因此,您可以在服务器上执行以下操作:

try {
  result = HTTP.call(
    'GET',
    'https://' + Meteor.settings.recurly.SUBDOMAIN + '.recurly.com/v2/invoices/' + invoiceId,
    {
      headers: {
        Authorization: "Basic " + (new Buffer(Meteor.settings.recurly.API_KEY)).toString('base64'),
        Accept: 'application/pdf'
      },
      npmRequestOptions: {
        encoding: null // will cause the result to be stored in a binary Buffer
      }
    }
  );

  // will write the file in binary mode
  fs.writeFile(outFileName, res.content, 'binary');
} catch (err) {
  result = e;
}

客户

客户端实现使用 XHR。

为了处理二进制响应,您需要将 XHR 的 responseType 更改为(最好)'blob'.

不幸的是,我认为无法在 Meteor 当前的 HTTP 包实现中获取二进制 blob,因为它期望响应具有 responseText.

您可以直接使用 XMLHttpRequest 对象,但您可能需要添加一些包装代码以支持快要死掉的浏览器(IE6,我在看着你!- 通常的 new ActiveXObject('Microsoft.XMLHttp'); 舞蹈)。

这可以使用类似下面的代码来实现:

var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.setRequestHeader('Authorization', ...);
xhr.responseType = 'blob'; // this is key

xhr.onload = function(e) {
  if (this.status == 200) {
    // this.response is a Blob. If you are sure that it is of the
    // correct content-type, you can use it to construct the URL directly
    let blob = new Blob([this.response], {type: 'application/pdf'});
    let url = URL.createObjectURL(blob);
    let a = document.createElement("a");
    a.href = url;
    a.download = "invoice.pdf";
    document.body.appendChild(a);
    ...
  }
};

xhr.send();

发送 XHR,将响应编码为二进制 Blob,并生成包含正确数据的 ObjectURL。