如何使用 javascript 从 Web 服务返回的二进制字符串构建 PDF 文件
Posted
技术标签:
【中文标题】如何使用 javascript 从 Web 服务返回的二进制字符串构建 PDF 文件【英文标题】:How to build PDF file from binary string returned from a web-service using javascript 【发布时间】:2012-10-04 06:26:48 【问题描述】:我正在尝试从作为 Ajax 请求响应的二进制流中构建 PDF 文件。
通过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 中。
我无权编辑 Web 服务实现。所以它必须是客户端的解决方案。有什么想法吗?
【问题讨论】:
我认为最好是每个人都编写一个将pdf文件下载到文件夹的服务,让网络服务器为静态文件提供服务。根据路径,构造文件的URL。 同意@SrinivasReddyThatiparthy。下载到临时文件并将文件提供给用户。如果绝对需要使用对象和嵌入标签的组合,您可以内联显示它 - 请参阅此问题:***.com/questions/1244788/embed-vs-object/… 为什么还要用js抓取数据?你不能用 location=我意识到这是一个相当老的问题,但这是我今天想出的解决方案:
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);
【讨论】:
你需要用ajax来做吗?您可以简单地将url作为href并直接下载(但前提是它是一个简单的get请求) 在我们的例子中使用简单的锚不是一个选项,AJAX 是必要的。 这似乎在 React 和其他由库维护您的 dom 的虚拟化 dom 库中无法始终如一地工作,您不应该以这种方式直接与它交互。 @Urasquirrel 现在也不是 2016 年了。时间改变一切。【参考方案2】:检测浏览器,Chrome 使用 Data-URI,其他浏览器使用PDF.js,如下所示。
PDFJS.getDocument(url_of_pdf)
.then(function(pdf)
return pdf.getPage(1);
)
.then(function(page)
// get a viewport
var scale = 1.5;
var viewport = page.getViewport(scale);
// get or create a canvas
var canvas = ...;
canvas.width = viewport.width;
canvas.height = viewport.height;
// render a page
page.render(
canvasContext: canvas.getContext('2d'),
viewport: viewport
);
)
.catch(function(err)
// deal with errors here!
);
【讨论】:
【参考方案3】:有没有像在文件系统上按顺序构建pdf文件的解决方案 让用户下载?
尝试将XMLHttpRequest
的responseType
设置为blob
,用a
元素的download
属性替换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();
【讨论】:
对我来说也很好,除了最后的removeChild(a)
,返回错误(类似于“找不到节点”)。所以我只是没有在window.onfocus
上触发此删除,而是直接将document.body.removeChild(a)
放在a.click()
之后。
@zezollo window
在click
事件之前获得关注?可能可以替换为document.body.appendChild(a);a.onclick = function() window.onfocus = function () document.body.removeChild(a); window.onfocus = null; a.click()
否,关闭 pdf 文件查看器(浏览器外部的软件,不是 pdf.js)后出现错误。我无法理解这个错误,因为在检查 html 代码时,它确实在正确的位置包含了 a
元素。无论如何,在click
之后立即删除孩子是否有问题?这真的很好。这也在这里使用:***.com/a/18197341/3926735
不,在元素上调用 .click()
后删除元素不是问题
对我来说,Windows 上最新的 chrome 似乎忽略了 a.click(),Linux 上的 firefox 也是如此【参考方案4】:
您可以使用PDF.js 从 javascript 创建 PDF 文件...它很容易编码...希望这能解决您的疑问!!!
问候!
【讨论】:
【参考方案5】:@alexandre 使用 base64 的答案可以解决问题。
为什么它适用于 IE 的解释在这里
https://en.m.wikipedia.org/wiki/Data_URI_scheme
在标题“格式”下显示的地方
某些浏览器(Chrome、Opera、Safari、Firefox)接受非标准的 如果同时提供 ;base64 和 ;charset ,则订购,而 Internet Explorer 要求字符集的规范必须在 base64 令牌。
【讨论】:
【参考方案6】:我在 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 服务或他正在使用服务器端语言。【参考方案7】:我改变了这个:
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>';
它对我有用。
【讨论】:
在安卓浏览器中工作? @Alexandre,此解决方案无法扩展。【参考方案8】:我最近看到另一个关于这个主题的问题 (streaming pdf into iframe using dataurl only works in chrome)。
我在 ast 中构建了 pdf 并将它们流式传输到浏览器。我首先使用 fdf 创建它们,然后使用我自己编写的 pdf 类创建它们 - 在每种情况下,pdf 都是从基于通过 url 传入的几个 GET 参数从 COM 对象检索的数据创建的。
通过查看您在 ajax 调用中收到的发送数据,您似乎就快到了。我已经有几年没有玩过代码了,也没有像我想要的那样记录它,但是 - 我认为你需要做的就是将 iframe 的目标设置为 url你从那里得到pdf。虽然这可能不起作用 - 输出 pdf 的文件也可能必须先输出 html 响应头。
简而言之,这是我使用的输出代码:
//We send to a browser
header('Content-Type: application/pdf');
if(headers_sent())
$this->Error('Some data has already been output, can\'t send PDF file');
header('Content-Length: '.strlen($this->buffer));
header('Content-Disposition: inline; filename="'.$name.'"');
header('Cache-Control: private, max-age=0, must-revalidate');
header('Pragma: public');
ini_set('zlib.output_compression','0');
echo $this->buffer;
因此,如果没有看到 ajax 调用的完整响应文本,我无法确定它是什么,尽管我倾向于认为输出您请求的 pdf 的代码可能只是等效的上面代码的最后一行。如果它是你可以控制的代码,我会尝试设置标题 - 这样浏览器就可以处理响应文本 - 你不必费心去做任何事情。
我只是为我想要的 pdf 构建了一个 url(一个时间表),然后创建了一个字符串,该字符串表示所需 sie、id 等的 iframe 的 html,该 iframe 使用构建的 url 作为 src。一旦我将 div 的内部 html 设置为构造的 html 字符串,浏览器就会要求提供 pdf,然后在收到时显示它。
function showPdfTt(studentId)
var url, tgt;
title = byId("popupTitle");
title.innerHTML = "Timetable for " + studentId;
tgt = byId("popupContent");
url = "pdftimetable.php?";
url += "id="+studentId;
url += "&type=Student";
tgt.innerHTML = "<iframe onload=\"centerElem(byId('box'))\" src='"+url+"' width=\"700px\" height=\"500px\"></iframe>";
编辑:忘了提 - 您可以通过这种方式发送二进制 pdf。它们包含的流不需要是 ascii85 或十六进制编码。我在 pdf 中的所有流上都使用了 flate,效果很好。
【讨论】:
以上是关于如何使用 javascript 从 Web 服务返回的二进制字符串构建 PDF 文件的主要内容,如果未能解决你的问题,请参考以下文章
如何使用javascript作为客户端和python作为Web服务器在websocets之间进行通信