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

时间:2012-10-13 18:57:57

标签: javascript jquery pdf cross-browser binary-data

我正在尝试使用二进制流构建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中。

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

8 个答案:

答案 0 :(得分:22)

  

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

尝试将responseType的{​​{1}}设置为XMLHttpRequest,将blob元素的download属性替换为a,以允许从window.open下载响应{1}} XMLHttpRequest文件

.pdf

答案 1 :(得分:12)

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

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);
}

答案 2 :(得分:4)

我改变了这个:

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>'; 

它对我有用。

答案 3 :(得分:3)

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

<?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);

?>

答案 4 :(得分:2)

我最近在这个主题上看到了另一个问题(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,它使用构造的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,它运行良好。

答案 5 :(得分:2)

@alexandre和base64的答案可以解决问题。

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

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

在标题'格式'下标明

  

某些浏览器(Chrome,Opera,Safari,Firefox)接受非标准版   如果两者都订购;提供base64和; charset,而Internet   Explorer要求charset的规范必须先于   base64令牌。

答案 6 :(得分: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!
});

答案 7 :(得分:1)

You can use PDF.js to create PDF files from javascript... it's easy to code... hope this solve your doubt!!!

Regards!