PDF Blob - 弹出窗口未显示内容

69

最近几天我一直在处理这个问题。尝试使用<embed src>标签来显示流的内容,但没有成功,于是我试图在一个新窗口中显示它。

新窗口只显示PDF控件enter image description here

有什么想法,为什么pdf的内容没有显示?

代码:

$http.post('/fetchBlobURL',{myParams}).success(function (data) {
    var file = new Blob([data], {type: 'application/pdf'});
    var fileURL = URL.createObjectURL(file);
    window.open(fileURL);
});

使用 $window(带有美元符号)。 - naXa stands with Ukraine
请查看我的问题和答案:http://stackoverflow.com/questions/41390623/post-complex-data-and-open-generated-pdf-in-new-window - Groppe
1
正如Groppe在答案中所示,createObjectURL在Internet Explorer上不起作用,您必须使用msSaveOrOpenBlob代替。https://dev59.com/pmAg5IYBdhLWcg3wE3jQ - gtamborero
10个回答

177

如果您想要从响应数据创建一个 blob,则需要将 responseType 设置为 arraybuffer

$http.post('/fetchBlobURL',{myParams}, {responseType: 'arraybuffer'})
   .success(function (data) {
       var file = new Blob([data], {type: 'application/pdf'});
       var fileURL = URL.createObjectURL(file);
       window.open(fileURL);
});

更多信息:向服务器发送和接收二进制数据


对我不起作用。在浏览器窗口中输入URL时,它可以工作,但通过$http.post方法无法工作。还有其他需要注意的事项吗?安全设置?浏览器类型和版本? - zszep
1
现在它正在工作。似乎arrayBuffer已经在angularjs的v.1.1中实现了。我使用的是v.1.08。当升级angularjs时,一切都正常工作。 - zszep
1
这对我来说几乎有效。我需要做同样的事情,但必须设置用户友好的URL或文件名。浏览器设置的UUID对我不起作用。您知道是否有一种方法可以在新标签页中打开PDF并在URL中设置文件名吗?谢谢! - Adrián Paredes
你能帮忙解决这个问题吗?https://stackoverflow.com/questions/63187758/issue-while-printing-pdf-files-from-blob-agnular - Sunil Garg
URL.createObjectURL()在Chrome 71中已被弃用。请参考https://developers.google.com/web/updates/2018/10/chrome-71-deps-rems,您不能再使用它了。 - Bojan Golubovic
从我的阅读中,我认为该函数并没有被一般性地弃用,而只是在与“MediaStream”结合使用时被弃用。该函数仍然存在于基于Chromium的浏览器中。因此,这个答案仍然是有效的。 - Andreas Linnert

31
如果您设置{ responseType: 'blob' },就不需要自己创建Blob。 您只需基于响应内容创建基于URL的url即可:
$http({
    url: "...",
    method: "POST",
    responseType: "blob"
}).then(function(response) {
    var fileURL = URL.createObjectURL(response.data);
    window.open(fileURL);
});

1
未捕获的 TypeError: 无法在“URL”上执行“createObjectURL”:找不到与提供的签名匹配的函数。 在对象成功 (enquiry.js:68) 在 j (jquery.js:2) 在对象 fireWith [作为 resolveWith] (jquery.js:2) 在 x (jquery.js:4) 在 XMLHttpRequest.<anonymous> (jquery.js:4) - Thakhani Tharage
@ThakhaniTharage 你用哪个浏览器?查看兼容性表 - Alena Kastsiukavets
你能帮忙解决这个问题吗?https://stackoverflow.com/questions/63187758/issue-while-printing-pdf-files-from-blob-agnular ? - Sunil Garg
当响应不存在且application/pdf是响应时怎么办? - Dalton Kim
@DaltonKim,你说的“response不在这里”是什么意思?response.data是null吗?虽然很久以前了,但我相信它可以正确处理。最坏的情况下,你可以添加if语句。 - Alena Kastsiukavets
显示剩余2条评论

10
我使用AngularJS v1.3.4。
HTML:
<button ng-click="downloadPdf()" class="btn btn-primary">download PDF</button>

JS 控制器:

'use strict';
angular.module('xxxxxxxxApp')
    .controller('MathController', function ($scope, MathServicePDF) {
        $scope.downloadPdf = function () {
            var fileName = "test.pdf";
            var a = document.createElement("a");
            document.body.appendChild(a);
            MathServicePDF.downloadPdf().then(function (result) {
                var file = new Blob([result.data], {type: 'application/pdf'});
                var fileURL = window.URL.createObjectURL(file);
                a.href = fileURL;
                a.download = fileName;
                a.click();
            });
        };
});

JS服务:

angular.module('xxxxxxxxApp')
    .factory('MathServicePDF', function ($http) {
        return {
            downloadPdf: function () {
            return $http.get('api/downloadPDF', { responseType: 'arraybuffer' }).then(function (response) {
                return response;
            });
        }
    };
});

Java REST Web Services - Spring MVC:

@RequestMapping(value = "/downloadPDF", method = RequestMethod.GET, produces = "application/pdf")
    public ResponseEntity<byte[]> getPDF() {
        FileInputStream fileStream;
        try {
            fileStream = new FileInputStream(new File("C:\\xxxxx\\xxxxxx\\test.pdf"));
            byte[] contents = IOUtils.toByteArray(fileStream);
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.parseMediaType("application/pdf"));
            String filename = "test.pdf";
            headers.setContentDispositionFormData(filename, filename);
            ResponseEntity<byte[]> response = new ResponseEntity<byte[]>(contents, headers, HttpStatus.OK);
            return response;
        } catch (FileNotFoundException e) {
           System.err.println(e);
        } catch (IOException e) {
            System.err.println(e);
        }
        return null;
    }

4

如果您的数据是字节数组,那么您不需要设置响应类型,但在将其传递给 blob 前,确保将其转换为 Uint8Array。

示例:

let byteArray = new Uint8Array(data)
let file = new Blob(
  [byteArray],
  {type: 'application/pdf'}
)

这对我有效。

如果您的数据不是字节数组,请确保将其转换为字节数组,并按照上述步骤进行操作,以使其正常工作。

//For example if your data is base-64 encoded string.
let byteChars = atob(data); //To decrypt data
let dataArray = = new Array(byteChars.length);
for(let i=0; i< byteChars.length; i++){
   dataArray[i] = byteChars.charCodeAt(i);
}
let byteArray = new Uint8Array(dataArray)
let file = new Blob(
  [byteArray],
  {type: 'application/pdf'}
)

3
// I used this code with the fpdf library.
// Este código lo usé con la libreria fpdf.

var datas = json1;
var xhr = new XMLHttpRequest();
xhr.open("POST", "carpeta/archivo.php");
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.responseType = "blob";
xhr.onload = function () {
    if (this.status === 200) {
        var blob = new Blob([xhr.response], {type: 'application/pdf'});
        const url = window.URL.createObjectURL(blob);
        window.open(url,"_blank");
        setTimeout(function () {
            // For Firefox it is necessary to delay revoking the ObjectURL
            window.URL.revokeObjectURL(datas)
                , 100
        })
    }
};
xhr.send("men="+datas);

2

我知道这已经有点过时了,但是因为这让我找到了正确的方向,所以我想分享一下我正在做什么,以防其他人也会来到这里。顺便说一句,我没有使用Angular。

用户可以查看或下载文件。提供了两个按钮或两个链接来进行选择。

<button type="button" class="btn btn-primary btn-sm show_tooltip download-form" title="Download File" data-formid="{{ @your-id }}" data-forcedownload="1">
    <i class="fas fa-file-download"></i>
</button>

<button type="button" class="btn btn-primary btn-sm show_tooltip download-form" title="View File" data-formid="{{ @your-id }}" data-forcedownload="0">
    <i class="fas fa-search"></i>
</button>

我正在使用jQuery与xhr2本地插件。这可以处理链接/按钮。

$('.download-form').click(function(event) {
    event.preventDefault();
    let fid = $(this).data('formid');
    let force_download = $(this).data('forcedownload');
    $.ajax({
          url: '/download',
          dataType: 'native',
          type: 'POST',
          xhrFields: {
              responseType: 'blob'
          },
          data: {
               //you can send any parameters via POST here
               personID: "{{ @personID }}",
               file_record_id: pfid,
               file_type: "contract_form",
               dept: "your-dept",
               file_category: "fcategory",
               force_download: force_download
           },
           success: function(blob, status, xhr){
                if (xhr.getResponseHeader('Custom-FileError')>1) {
                      alertify.error(xhr.getResponseHeader('Custom-ErrorMsg'));
                }else{
                      //I thought this would work when viewing the PDF but it does not.
                      blob.name = xhr.getResponseHeader('Custom-FileName');
                      var fileURL = URL.createObjectURL(blob);
                      if (xhr.getResponseHeader('Custom-ForceDownload')==1) {
                            window.open(fileURL);
                            var link=document.createElement('a');
                            link.href=window.URL.createObjectURL(blob);
                            link.download=xhr.getResponseHeader('Custom-FileName');
                            link.click();
                       }else{
                            file_modal(fileURL,'Any Title');
                       }
                       
                   }
              }
           })
});

然后,还需要一些用于模态框的JavaScript代码。

function file_modal(blob,the_title)
{

    let spinner = "<div class='text-center'><i class='fa fa-spinner fa-spin fa-5x fa-fw'></i></div>";
    $("#modal_static_label").html('Loading');
    $("#modal_static .modal-body").html(spinner);
    if (blob.length > 1) {
        $("#modal_static").modal("show");
        $("#modal_static_label").html(the_title);
        $("#modal_static .modal-body").empty().append('<iframe src='+blob+' width="100%" height="500px" style="border:none;"></iframe>');
    }else{
         $("#modal_static .modal-body").empty().html('File error');
    }
    $("#modal_static .modal-footer").html('<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>');
        
}

在服务器端,您需要像这样发送自定义标头 [PHP]


header("Content-length: $file_size");
header("Custom-FileError: 0");
header("Custom-FileName: ".$this->params['original_filename']);
header("Custom-ForceDownload: ".$this->params['force_download']);
header('Content-Type: '.$web->mime($this->full_path.$this->new_file_name));
readfile($this->full_path.$this->new_file_name);

如果用户点击“查看”,则会显示模态框以显示PDF文件,如果他们点击“下载”,则下载窗口将出现,并显示您选择的文件名。我已经测试了小于10MB的PDF文件,它能够按照预期工作。
希望这对某些人有所帮助。

xhrFields: { responseType: 'blob' },这样修改解决了我的问题。 - Math is Hard

1

我苦苦挣扎了好几天,最终找到了以下解决方案。我需要在新窗口中使用window.print()来生成PDF文件。

 var xhr = new XMLHttpRequest();
      xhr.open('GET', pdfUrl, true);
      xhr.responseType = 'blob';

      xhr.onload = function(e) {
        if (this['status'] == 200) {          
          var blob = new Blob([this['response']], {type: 'application/pdf'});
          var url = URL.createObjectURL(blob);
          var printWindow = window.open(url, '', 'width=800,height=500');
          printWindow.print()
        }
      };

      xhr.send();

关于在新窗口中加载PDF和打印的一些注意事项。

  • 通过iframe在新窗口中加载pdf将起作用,但如果url是外部url,则打印将无法工作。
  • 必须允许浏览器弹出窗口,才能使其起作用。
  • 如果您尝试从外部url加载iframe并尝试window.print(),则会得到空白打印或排除iframe的元素。但您可以手动触发打印,这将起作用。

这对我没有用,我得到了enquiry.js:85 Uncaught TypeError: Cannot read property 'print' of null at XMLHttpRequest.xhr.onload - Thakhani Tharage
新窗口打开了吗?这个示例之前对我有效过。 - Krishnadas PC
不,它不是。 - Thakhani Tharage
PDF的URL必须来自同一服务器,否则会显示CORS错误。 - Krishnadas PC
请查看工作示例链接 https://pdf-print-pckrishnadas88.c9users.io/hello-world.html。请注意,由于这是一个免费帐户,它将仅可用几个小时。 - Krishnadas PC
打印?只需打开PDF。 - shinriyo

0
问题是,它没有被转换为正确的格式。使用函数"printPreview(binaryPDFData)"获取二进制pdf数据的打印预览对话框。 如果您不想打开打印对话框,可以注释脚本部分。
printPreview = (data, type = 'application/pdf') => {
    let blob = null;
    blob = this.b64toBlob(data, type);
    const blobURL = URL.createObjectURL(blob);
    const theWindow = window.open(blobURL);
    const theDoc = theWindow.document;
    const theScript = document.createElement('script');
    function injectThis() {
        window.print();
    }
    theScript.innerHTML = `window.onload = ${injectThis.toString()};`;
    theDoc.body.appendChild(theScript);
};

b64toBlob = (content, contentType) => {
    contentType = contentType || '';
    const sliceSize = 512;
     // method which converts base64 to binary
    const byteCharacters = window.atob(content); 

    const byteArrays = [];
    for (let offset = 0; offset < byteCharacters.length; offset += sliceSize) {
        const slice = byteCharacters.slice(offset, offset + sliceSize);
        const byteNumbers = new Array(slice.length);
        for (let i = 0; i < slice.length; i++) {
            byteNumbers[i] = slice.charCodeAt(i);
        }
        const byteArray = new Uint8Array(byteNumbers);
        byteArrays.push(byteArray);
    }
    const blob = new Blob(byteArrays, {
        type: contentType
    }); // statement which creates the blob
    return blob;
};

-1

最终我使用以下代码下载了我的PDF文件

function downloadPdfDocument(fileName){

var req = new XMLHttpRequest();
req.open("POST", "/pdf/" + fileName, true);
req.responseType = "blob";
fileName += "_" + new Date() + ".pdf";

req.onload = function (event) {

    var blob = req.response;

    //for IE
    if (window.navigator && window.navigator.msSaveOrOpenBlob) {
        window.navigator.msSaveOrOpenBlob(blob, fileName);
    } else {

        var link = document.createElement('a');
        link.href = window.URL.createObjectURL(blob);
        link.download = fileName;
        link.click();
    }
};

req.send();

}


它开始下载。 - shinriyo
这就是我想要的,这就是为什么它在一个函数中,这样我可以在准备好下载时调用它。不确定你为什么投了反对票。 - Thakhani Tharage
这就是我想要的,也是 :-) - undefined

-3

let url = "data:application/pdf;base64," + base64PdfFile
fetch(url)
.then(res => res.blob())
.then(response => {
console.log(response);
let url = window.URL.createObjectURL(response);
let link = document.createElement("a");
link.target = "_blank";
link.href = url;
document.body.appendChild(link);
link.click();
});

注意:this.file 应该是您的 base64 文件。

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