使用JavaScript在客户端将Base64字符串保存为PDF文件

79

我的问题是:我从服务器获取了一个 PDF 文件的 base64 字符串,我想要使用这个字符串来直接在浏览器中显示 PDF,或者在点击链接时提供“另存为...”选项。以下是我正在使用的代码:

<!doctype>
<html>
<head>
   <title>jsPDF</title>
   <script type="text/javascript" src="../libs/base64.js"></script>
   <script type="text/javascript" src="../libs/sprintf.js"></script>
   <script type="text/javascript" src="../jspdf.js"></script>

       <script type="text/javascript">

        function demo1() {
            jsPDF.init();
            jsPDF.addPage();
            jsPDF.text(20, 20, 'Hello world!');
            jsPDF.text(20, 30, 'This is client-side Javascript, pumping out a PDF.');

            // Making Data URI
            var out = jsPDF.output();
            var url = 'data:application/pdf;base64,' + Base64.encode(out);

            document.location.href = url;
         }
    </script>
</head>
<body>

<a href="javascript:demo1()">Run Code</a>

</body>
</html>

在Chrome和Safari中正常工作。Firefox识别PDF文件,但由于在这种情况下data-URI中没有扩展名,因此不显示它。我坚持的原因是,如果Chrome和Safari可以使其正常工作,那么还必须有解决方案适用于Firefox和IE。

我知道有一些相关问题,但并不是完全相同的问题,而且现在也有点过时了。我知道一种解决方法是在服务器端生成PDF文件,但我想在客户端生成它。

请问聪明的人们,是否可以通过某些技巧或附加的JS下载插件来实现?


1
你好,有没有人能回答一下这个问题?也许是John Resig吧;-) - owsata
@owsata,我也遇到了同样的问题!它只是打开了窗口!你找到解决办法了吗?请告诉我们。谢谢。 - Fabio Milheiro
1
@FabioMilheiro 没有找到任何有用的东西。最终结果是,因为浏览器处理data:application这个想法的方式不同,所以一开始就没有什么用处。所以最后的办法是从服务器发送一个准备好的pdf文件。 - owsata
1
这个解决了我的问题,也让我节省了周末。 - Shubham
6个回答

72

您可以使用此函数从base64下载文件。

function downloadPDF(pdf) {
const linkSource = `data:application/pdf;base64,${pdf}`;
const downloadLink = document.createElement("a");
const fileName = "abc.pdf";
downloadLink.href = linkSource;
downloadLink.download = fileName;
downloadLink.click();}

这段代码会创建一个带有href和download属性的锚点标签。如果您想使用按钮,则可以在您的按钮上调用click方法。

希望这能对您有所帮助,谢谢。


我认为提到一下,如果没有用户交互活动(如真正的点击),这可能现在不起作用会很有用。您仍然需要用户单击某些内容才能激活此功能。 - Valerio Bozz

46
您可以创建以下示例中所示的锚点来下载base64格式的PDF文件:
<a download="PDF Title" href="pdfData">Download PDF document</a>

其中 pdfData 是您的 base64 编码的 PDF,如下所示:

data:application/pdf;base64,JVBERi0xLjQKJcOkw7zDtsOfCjIgMCBvYmoKPDwvTGVuZ3RoIDMgMCBSL0ZpbHRlci9GbGF0ZURlY29kZT4+CnN0cmVhbQp4nO1cyY4ktxG911fUWUC3kjsTaBTQ1Ytg32QN4IPgk23JMDQ2LB/0+2YsZAQzmZk1PSPIEB...

2
你提供的信息帮助我在Salesforce Lightning中完成了这个功能,使用此字符串,可以使文件在按钮点击时下载。 - Subinoy
3
在C#中,当您返回一个FileContentResult时,它会保持文件以base64编码的形式,但是没有任何指示和MIME类型...因此,为了在客户端实现它,我不得不在字符串前面添加"data:application/pdf;base64,",这样它就可以正常工作了! - Божидар Йовчев
2
这帮助我避免了创建额外的后端端点来下载文件,谢谢!! - EVIL
1
@OssSilva 很高兴听到这个消息! - Gabriel Cerutti

26

你应该可以使用下面的方法下载文件:

window.open("data:application/pdf;base64," + Base64.encode(out));

3
不,Chrome和FF直接下载它,但不分配扩展名。Safari在浏览器中打开PDF并显示它,哇。IE打开一个标题为“application/octet-stream”的新选项卡,除此之外什么也没有... - owsata
我使用数据结构进行了直接重定向,但它需要一个容器或机制将数据解析为可用格式(例如:iFrame)。然而,我尚未按照规定测试此机制。 - Reinhard Behrens

19

function dataURItoBlob(dataURI) {
      const byteString = window.atob(dataURI);
      const arrayBuffer = new ArrayBuffer(byteString.length);
      const int8Array = new Uint8Array(arrayBuffer);
      for (let i = 0; i < byteString.length; i++) {
        int8Array[i] = byteString.charCodeAt(i);
      }
      const blob = new Blob([int8Array], { type: 'application/pdf'});
      return blob;
    }

// data should be your response data in base64 format

const blob = this.dataURItoBlob(data);
const url = URL.createObjectURL(blob);

// to open the PDF in a new window
window.open(url, '_blank');


2
我在互联网上寻找了几种解决方案,但只有这个对我有效,感谢Manas Sahu先生。 - Ricardo Lerma
1
很高兴为你效劳,@RicardoLerma。 - Manas
1
在我的 React 前端应用中有效。 - Alex Baban
1
请修复我的Safari / iOS问题!谢谢。 - E P

18

我知道这个问题很旧了,但我也想完成它,并在查找时遇到了它。对于Internet Explorer,我使用了来自此处的代码来保存Blob。有许多关于如何从base64字符串创建blob的结果在这个网站上,所以这不是我的代码,我只是记不起具体的来源:

function b64toBlob(b64Data, contentType) {
    contentType = contentType || '';
    var sliceSize = 512;
    b64Data = b64Data.replace(/^[^,]+,/, '');
    b64Data = b64Data.replace(/\s/g, '');
    var byteCharacters = window.atob(b64Data);
    var byteArrays = [];

    for (var offset = 0; offset < byteCharacters.length; offset += sliceSize) {
        var slice = byteCharacters.slice(offset, offset + sliceSize);

        var byteNumbers = new Array(slice.length);
        for (var i = 0; i < slice.length; i++) {
            byteNumbers[i] = slice.charCodeAt(i);
        }

        var byteArray = new Uint8Array(byteNumbers);

        byteArrays.push(byteArray);
    }

    var blob = new Blob(byteArrays, {type: contentType});
    return blob;

使用链接的文件保存器:

if (window.saveAs) { window.saveAs(blob, name); }
    else { navigator.saveBlob(blob, name); }

文件保存器在Safari中也失败了 :( - jrh
我在以下链接发布了解决方案:https://dev59.com/YFsW5IYBdhLWcg3w8q1S#45669785该解决方案在IE、Chrome和Firefox中都能正常工作。如果这个解决方案对你有帮助,请尝试给它投票。 - Sajjad Ali Khan

2
你不需要使用任何库来完成这个任务,JavaScript已经支持了。以下是我的端到端解决方案。
const xhr = new XMLHttpRequest();
xhr.open('GET', 'your-end-point', true);
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
xhr.responseType = 'blob';
xhr.onreadystatechange = function () {
    if (this.readyState == 4 && this.status == 200) {
       if (window.navigator.msSaveOrOpenBlob) {
           window.navigator.msSaveBlob(this.response, "fileName.pdf");
        } else {
           const downloadLink = window.document.createElement('a');
           const contentTypeHeader = xhr.getResponseHeader("Content-Type");
           downloadLink.href = window.URL.createObjectURL(new Blob([this.response], { type: contentTypeHeader }));
           downloadLink.download = "fileName.pdf";
           document.body.appendChild(downloadLink);
           downloadLink.click();
           document.body.removeChild(downloadLink);
        }
    }
};
xhr.send(null);

这也适用于 .xls.zip 文件。您只需要将文件名更改为 fileName.xlsfileName.zip。这取决于您的情况。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接