使用jQuery Ajax下载PDF文件

47

我想要下载一个包含在jQuery Ajax响应中的PDF文件。我尝试了这个解决方案,但是我的代码总是得到一个空白的PDF。

$(document).on('click', '.download-ss-btn', function () {

    $.ajax({
        type: "POST",
        url: 'http://127.0.0.1:8080/utils/json/pdfGen',
        data: {
            data: JSON.stringify(jsonData)
        }

    }).done(function (data) {
        var blob = new Blob([data]);
        var link = document.createElement('a');
        link.href = window.URL.createObjectURL(blob);
        link.download = "Sample.pdf";
        link.click();
    });


});

尝试将jQuery.ajax()替换为XMLHttpRequest,请参见https://dev59.com/mmcs5IYBdhLWcg3waTND - guest271314
2
为什么你需要使用AJAX呢?下载文件不使用AJAX更容易、更可靠。 - Darin Dimitrov
您不能使用json响应生成PDF。它应该是一个HTML响应,并且它不适用于所有浏览器。在给定的示例中,它返回Url,即HTML响应。 - Parth Trivedi
1
我在这里使用一个Web服务来获取JSON数据格式化的PDF文件。响应PDF文件数据以“%PDF-1.4”开头。 - azhar
你需要在 success.done 中编写代码,并注明你正在测试的 浏览器 版本。Blob([data]) 在某些浏览器中无法正常工作。 - Parth Trivedi
当我从Ajax响应中控制台输出数据时,我可以正确地获取文件数据。 - azhar
5个回答

107

在使用 AJAX 请求时,jQuery 有一些加载二进制数据的问题,因为它还没有实现一些 HTML5 XHR v2 的功能,可以参考这个 增强请求 和这个 讨论

鉴于此,您有两种解决方案:

第一种解决方案,放弃使用 jQuery 并使用 XMLHTTPRequest

使用本机 HTMLHTTPRequest,以下是执行所需操作的代码

  var req = new XMLHttpRequest();
  req.open("GET", "/file.pdf", true);
  req.responseType = "blob";

  req.onload = function (event) {
    var blob = req.response;
    console.log(blob.size);
    var link=document.createElement('a');
    link.href=window.URL.createObjectURL(blob);
    link.download="Dossier_" + new Date() + ".pdf";
    link.click();
  };

  req.send();

第二种解决方案,使用jquery-ajax-native插件

该插件可以在这里找到,并且可以用于补充JQuery中缺失的XHR V2功能,以下是如何使用它的示例代码。

$.ajax({
  dataType: 'native',
  url: "/file.pdf",
  xhrFields: {
    responseType: 'blob'
  },
  success: function(blob){
    console.log(blob.size);
      var link=document.createElement('a');
      link.href=window.URL.createObjectURL(blob);
      link.download="Dossier_" + new Date() + ".pdf";
      link.click();
  }
});

8
要使其在Firefox中起作用,您需要在单击链接之前添加“document.body.appendChild(link);” :) - Arnaud Rochez
1
运行良好,但我发现你需要使用window.navigator.msSaveBlob(blob, filename);才能在IE11中使其正常工作,如@anonymous所示。 - Perry
这正是我所需要的。xhrFields: { responseType: 'blob' }, - sho
1
InvalidStateError: 无法从XMLHttpRequest读取'responseText'属性:仅当对象的'responseType'为''或'text'(为'blob')时,该值才可访问。 - Ryan Dooley
第一种解决方案对我有效,但是在下载PDF文件后,当我尝试打开文件时,它会提示错误,说“Adobe Acrobat Reader无法打开......因为它不是受支持的文件之一,等等......” - Waseem
显示剩余3条评论

23

我是新手,大部分代码都是从谷歌搜索中获取的。通过以下代码(尝试和错误)使我的pdf下载工作正常。感谢上面提供的代码提示(xhrFields)。

$.ajax({
            cache: false,
            type: 'POST',
            url: 'yourURL',
            contentType: false,
            processData: false,
            data: yourdata,
             //xhrFields is what did the trick to read the blob to pdf
            xhrFields: {
                responseType: 'blob'
            },
            success: function (response, status, xhr) {

                var filename = "";                   
                var disposition = xhr.getResponseHeader('Content-Disposition');

                 if (disposition) {
                    var filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
                    var matches = filenameRegex.exec(disposition);
                    if (matches !== null && matches[1]) filename = matches[1].replace(/['"]/g, '');
                } 
                var linkelem = document.createElement('a');
                try {
                                           var blob = new Blob([response], { type: 'application/octet-stream' });                        

                    if (typeof window.navigator.msSaveBlob !== 'undefined') {
                        //   IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed."
                        window.navigator.msSaveBlob(blob, filename);
                    } else {
                        var URL = window.URL || window.webkitURL;
                        var downloadUrl = URL.createObjectURL(blob);

                        if (filename) { 
                            // use HTML5 a[download] attribute to specify filename
                            var a = document.createElement("a");

                            // safari doesn't support this yet
                            if (typeof a.download === 'undefined') {
                                window.location = downloadUrl;
                            } else {
                                a.href = downloadUrl;
                                a.download = filename;
                                document.body.appendChild(a);
                                a.target = "_blank";
                                a.click();
                            }
                        } else {
                            window.location = downloadUrl;
                        }
                    }   

                } catch (ex) {
                    console.log(ex);
                } 
            }
        });

你是我的英雄。谢谢 :) - Kamlesh Kumar
1
5个小时和大约30个其他属性添加到.ajax({})中...并且xhrFields修复了它。非常感谢! - Samyok Nepal

7
您可以通过HTML5轻松完成此操作:
var link = document.createElement('a');
link.href = "/WWW/test.pdf";
link.download = "file_" + new Date() + ".pdf";
link.click();
link.remove();

6

对于那些寻求更现代方法的人,您可以使用 fetch API。以下示例显示如何下载 PDF 文件。只需使用以下代码即可轻松完成。

fetch(url, {
    body: JSON.stringify(data),
    method: 'POST',
    headers: {
        'Content-Type': 'application/json; charset=utf-8'
    },
})
.then(response => response.blob())
.then(response => {
    const blob = new Blob([response], {type: 'application/pdf'});
    const downloadUrl = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = downloadUrl;
    a.download = "file.pdf";
    document.body.appendChild(a);
    a.click();
})

我认为这种方法比其他的XMLHttpRequest解决方案更易于理解。此外,它具有类似于jQuery方法的语法,无需添加任何额外的库。

当然,我建议检查您正在开发的浏览器,因为这种新方法在IE上不起作用。您可以在以下[链接][1]中找到完整的浏览器兼容性列表。

重要提示:在此示例中,我向侦听给定url的服务器发送JSON请求。必须设置此url,在我的示例中,我假设您已经了解此部分。另外,请考虑您的请求所需的标头。由于我发送的是JSON请求,因此必须添加Content-Type标头并将其设置为application/json; charset=utf-8,以让服务器知道它将接收的请求类型。


数据未定义!但是当我删除第二行时它可以工作。 - Hisham Dalal
1
那是因为你需要定义它,除非你的请求不需要主体。 - Alain Cruz
第二个 .then() 接收的是 Blob,而不是 Response - djvg

0

试试这个:

$(document).on('click', '.download-ss-btn', function () {

    $.ajax({
        type: "POST",
        url: 'http://127.0.0.1:8080/utils/json/pdfGen',
        data: {
            data: JSON.stringify(jsonData)
        },
        headers: {
            'Content-Type': 'application/json; charset=utf-8'
        }

    }).done(function (data) {
        let binaryString = window.atob(data);
        let binaryLen = binaryString.length;
        let bytes = new Uint8Array(binaryLen);

        for (let i = 0; i < binaryLen; i++) {
            let ascii = binaryString.charCodeAt(i);
            bytes[i] = ascii;
        }
        var blob = new Blob([data], {type: "application/pdf"});
        var link = document.createElement('a');
        link.href = window.URL.createObjectURL(blob);
        link.download = "Sample.pdf";
        link.click();
    });
});

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