如何使用javascript从Web服务返回的二进制字符串构建PDF文件

IT技术 javascript jquery pdf cross-browser binary-data
2021-01-22 16:20:09

我正在尝试从一个二进制流中构建一个 PDF 文件,我收到它作为来自 Ajax 请求的响应。

通过XmlHttpRequest我收到以下数据:

%PDF-1.4....
.....
....hole data representing the file
....
%% EOF

到目前为止,我尝试的是通过data:uri. 现在,它没有任何问题,并且运行良好。不幸的是,它在 IE9 和 Firefox 中不起作用。一个可能的原因可能是 FF 和 IE9 在使用data-uri.

现在,我正在寻找适用于所有浏览器的任何解决方案。这是我的代码:

// responseText encoding 
pdfText = $.base64.decode($.trim(pdfText));

// Now pdfText contains %PDF-1.4 ...... data...... %%EOF

var winlogicalname = "detailPDF";
var winparams = 'dependent=yes,locationbar=no,scrollbars=yes,menubar=yes,'+
            'resizable,screenX=50,screenY=50,width=850,height=1050';

var htmlText = '<embed width=100% height=100%'
                     + ' type="application/pdf"'
                     + ' src="data:application/pdf,'
                     + escape(pdfText)
                     + '"></embed>'; 

                // Open PDF in new browser window
                var detailWindow = window.open ("", winlogicalname, winparams);
                detailWindow.document.write(htmlText);
                detailWindow.document.close();

正如我所说,它适用于 Opera 和 Chrome(Safari 尚未经过测试)。使用 IE 或 FF 将打开一个空白的新窗口。

有没有像在文件系统上构建 PDF 文件以便让用户下载它的解决方案?我需要适用于所有浏览器的解决方案,至少适用于 IE、FF、Opera、Chrome 和 Safari。

我无权编辑网络服务实现。所以它必须是客户端的解决方案。有任何想法吗?

6个回答

是否有任何解决方案,例如在文件系统上构建 pdf 文件以让用户下载它?

尝试设置responseTypeXMLHttpRequestblob,用 download在属性a元素window.open,使响应的下载从XMLHttpRequest作为.pdf文件

var request = new XMLHttpRequest();
request.open("GET", "/path/to/pdf", true); 
request.responseType = "blob";
request.onload = function (e) {
    if (this.status === 200) {
        // `blob` response
        console.log(this.response);
        // create `objectURL` of `this.response` : `.pdf` as `Blob`
        var file = window.URL.createObjectURL(this.response);
        var a = document.createElement("a");
        a.href = file;
        a.download = this.response.name || "detailPDF";
        document.body.appendChild(a);
        a.click();
        // remove `a` following `Save As` dialog, 
        // `window` regains `focus`
        window.onfocus = function () {                     
          document.body.removeChild(a)
        }
    };
};
request.send();
不,关闭 pdf 文件查看器(浏览器外的软件,而不是 pdf.js)后会出现错误。我无法理解这个错误,因为在检查 html 代码时,它确实a在正确的位置包含了该元素。无论如何,这是在之后立即移除孩子的问题click吗?这真的很好用。这也在这里使用:stackoverflow.com/a/18197341/3926735
2021-03-17 16:20:09
对我来说,windows 上的最新 chrome 似乎忽略了 a.click(),对于 linux 上的 firefox 也是如此
2021-03-20 16:20:09
对我来说也很好用,除了 final removeChild(a),什么返回了错误(类似于“找不到节点”)。所以,我只是不触发此删除window.onfocus,而是直接把document.body.removeChild(a)右后a.click()
2021-03-30 16:20:09
@zezollowindowclick活动前获得关注大概可以换成document.body.appendChild(a);a.onclick = function() { window.onfocus = function () { document.body.removeChild(a); window.onfocus = null; } } a.click()
2021-04-04 16:20:09
不,不是在调用.click()元素后删除元素的问题
2021-04-06 16:20:09

我意识到这是一个相当古老的问题,但这是我今天想出的解决方案:

doSomethingToRequestData().then(function(downloadedFile) {
  // create a download anchor tag
  var downloadLink      = document.createElement('a');
  downloadLink.target   = '_blank';
  downloadLink.download = 'name_to_give_saved_file.pdf';

  // convert downloaded data to a Blob
  var blob = new Blob([downloadedFile.data], { type: 'application/pdf' });

  // create an object URL from the Blob
  var URL = window.URL || window.webkitURL;
  var downloadUrl = URL.createObjectURL(blob);

  // set object URL as the anchor's href
  downloadLink.href = downloadUrl;

  // append the anchor to document body
  document.body.append(downloadLink);

  // fire a click event on the anchor
  downloadLink.click();

  // cleanup: remove element and revoke object URL
  document.body.removeChild(downloadLink);
  URL.revokeObjectURL(downloadUrl);
}
@Urasquirrel 也不再是 2016 年了。时间会改变一切。
2021-03-13 16:20:09
在我们的案例中,使用简单的锚点不是一种选择,AJAX 是必要的。
2021-04-04 16:20:09
这在 React 和其他虚拟化 dom 库中似乎并不一致,其中库维护您的 dom,您不应该以这种方式直接与它交互。
2021-04-04 16:20:09
你需要用ajax来做吗?您可以简单地将 url 设为 href 并直接下载(但前提是它是一个简单的 get 请求)
2021-04-08 16:20:09

我改变了这个:

var htmlText = '<embed width=100% height=100%'
                 + ' type="application/pdf"'
                 + ' src="data:application/pdf,'
                 + escape(pdfText)
                 + '"></embed>'; 

var htmlText = '<embed width=100% height=100%'
                 + ' type="application/pdf"'
                 + ' src="data:application/pdf;base64,'
                 + escape(pdfText)
                 + '"></embed>'; 

它对我有用。

在 Android 浏览器中工作?
2021-03-13 16:20:09
@Alexandre,此解决方案无法扩展。
2021-04-07 16:20:09

@alexandre 对 base64 的回答可以解决问题。

为什么它适用于 IE 的解释在这里

https://en.m.wikipedia.org/wiki/Data_URI_scheme

在标题“格式”下,它说

如果同时提供 ;base64 和 ;charset,则某些浏览器(Chrome、Opera、Safari、Firefox)接受非标准排序,而 Internet Explorer 要求字符集的规范必须位于 base64 标记之前。

我在 PHP 中工作并使用一个函数来解码从服务器发回的二进制数据。我将信息提取到一个简单的 file.php 中,并通过我的服务器查看该文件,所有浏览器都显示 pdf 人工制品。

<?php
   $data = 'dfjhdfjhdfjhdfjhjhdfjhdfjhdfjhdfdfjhdf==blah...blah...blah..'

   $data = base64_decode($data);
    header("Content-type: application/pdf");
    header("Content-Length:" . strlen($data ));
    header("Content-Disposition: inline; filename=label.pdf");
    print $data;
    exit(1);

?>
-1. 有趣但不是用户所问的。他特别要求从 Web 服务到 Javascript 在客户端完成,不要假设用户甚至控制 Web 服务或他正在使用服务器端语言。
2021-04-09 16:20:09