PDF Blob - Pop up window not showing content
Asked Answered
P

11

70

I have been working on this problem for the last few days. With no luck on trying to display the stream on <embed src> tag, I just tried to display it on a new window.

The new window shows PDF controls only enter image description here)

Any idea why the content of the pdf is not showing?

CODE:

$http.post('/fetchBlobURL',{myParams}).success(function (data) {
    var file = new Blob([data], {type: 'application/pdf'});
    var fileURL = URL.createObjectURL(file);
    window.open(fileURL);
});
Performative answered 12/2, 2014 at 13:35 Comment(3)
Use $window (with a dollar sign).Footpoundsecond
See my question and answer here: #41391123Ribbon
As Groppe shows on the answer, createObjectURL doesn't work on Internet Explorer, you have to use msSaveOrOpenBlob instead. #24007573Lannylanolin
H
179

You need to set the responseType to arraybuffer if you would like to create a blob from your response data:

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

more information: Sending_and_Receiving_Binary_Data

Herren answered 12/2, 2014 at 14:19 Comment(6)
Doesn't work for me. When typing the URL in the browser window, it works, but not via the $http.post method. Is there anything else to pay attention to? Security settings? Browser type and version?Foreandaft
It is working now. Seems the arrayBuffer was implemented as of v.1.1 in angularjs. I had v.1.08. When upgrading angularjs, everything worked just fine.Foreandaft
This almost works for me. I need to do the same, but I have to set a user-friendly URL or fileName. The UUID that browser sets it doesn't work for me. Do you know if there is a way to open a PDF in a new tab and set the file name in the URL? Thanks!Adipocere
Can you help on this #63188258 ?Lipkin
URL.createObjectURL() is deprecated from Chrome 71. developers.google.com/web/updates/2018/10/chrome-71-deps-rems You can not use it anymore.Neckar
@BojanGolubovic from how I read this the function has not been deprecated in general but only in combination with MediaStream. The function still exists today in Chromium based browsers. So, this answer should still be valid.Soothe
I
31

If you set { responseType: 'blob' }, no need to create Blob on your own. You can simply create url based with response content:

$http({
    url: "...",
    method: "POST",
    responseType: "blob"
}).then(function(response) {
    var fileURL = URL.createObjectURL(response.data);
    window.open(fileURL);
});
Isochronal answered 19/7, 2017 at 13:33 Comment(7)
Excellent! This worked for me after spending many hours trying to display the pdf content.Affricate
ncaught TypeError: Failed to execute 'createObjectURL' on 'URL': No function was found that matched the signature provided. at Object.success (enquiry.js:68) at j (jquery.js:2) at Object.fireWith [as resolveWith] (jquery.js:2) at x (jquery.js:4) at XMLHttpRequest.<anonymous> (jquery.js:4)Slob
@ThakhaniTharage which browser do you use? Check compatibility tableIsochronal
Can you help on this #63188258 ?Lipkin
how about when the response is not there and application/pdf is the responseUralite
@DaltonKim what do you mean response is not here? response.data is null? It was long time ago, but i believe it handles it correctly. Worst case you can add if statementIsochronal
Uncaught (in promise) TypeError: Failed to execute 'createObjectURL' on 'URL': Overload resolution failed.. The createObjectURL cannot understand the mimetype of contentAmity
W
10

I use AngularJS v1.3.4

HTML:

<button ng-click="downloadPdf()" class="btn btn-primary">download PDF</button>

JS controller:

'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 services:

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;
    }
Wyman answered 22/5, 2015 at 20:28 Comment(0)
R
4

You are not required to set the response type if your data is a byte array, make sure you convert it to Uint8Array before passing it to blob.

Example:

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

It works for me.

If your data is not byteArray, make sure to convert it to byteArray and follow above-mentioned steps to make it work.

//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'}
)
Radiotelephony answered 29/4, 2021 at 16:38 Comment(0)
B
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);
Breccia answered 26/2, 2020 at 16:32 Comment(0)
P
2

I know this is old but since this pointed me in the right direction, I thought I would share what I am doing in case someone else lands here. I am not using Angular btw.

The user can view or download the file. The choice is given with 2 buttons or 2 links

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

I am using jQuery with the native plugin for xhr2. This handles the link/buttons

$('.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');
                       }
                       
                   }
              }
           })
});

Then, some more javascript for the modal

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

On the server side you will need to send custom headers like this [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);

If the user clicks "view", a modal will display the PDF if they click "download", the download window will show up with the filename of your choosing. I have tested this with PDF files less than 10mb and it works as expected.

I hope someone finds this useful.

Peshitta answered 12/3, 2021 at 16:48 Comment(1)
``` xhrFields: { responseType: 'blob' }, ``` fixed it for me.Reckoning
R
1

I have been struggling for days finally the solution which worked for me is given below. I had to make the window.print() for PDF in new window needs to work.

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

Some notes on loading PDF & printing in a new window.

  • Loading pdf in a new window via an iframe will work, but the print will not work if url is an external url.
  • Browser pop ups must be allowed, then only it will work.
  • If you try to load iframe from external url and try window.print() you will get empty print or elements which excludes iframe. But you can trigger print manually, which will work.
Rossiter answered 25/9, 2018 at 18:25 Comment(6)
This did not work for me i got enquiry.js:85 Uncaught TypeError: Cannot read property 'print' of null at XMLHttpRequest.xhr.onloadSlob
Does new window opened? This sample has been worked for me earlier.Rossiter
Nop it does notSlob
pdf URL must be from the same server. Otherwise, it will show CORS error.Rossiter
See the working sample link pdf-print-pckrishnadas88.c9users.io/hello-world.html. Please note it will be available for few hours as it is a free account.Rossiter
print? just open PDF.Overmodest
L
0

problem is, it is not converted to proper format. Use function "printPreview(binaryPDFData)" to get print preview dialog of binary pdf data. you can comment script part if you don't want print dialog open.

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;
};
Lizarraga answered 3/9, 2019 at 7:45 Comment(0)
S
-1

I ended up just downloading my pdf using below code

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

}

Slob answered 6/11, 2018 at 17:40 Comment(3)
it starts download.Overmodest
That's what I wanted, That's why it's in a function so that I can call it when I am ready to do the download. Not sure why you downvotedSlob
that is what i wanted also :-)Elaterite
C
-1

Add

responseType: "arraybuffer"

in API config.
This features make resizing ArrayBuffers more efficient. Otherwise, you have to make a copy of the buffer with a new size.

Cartesian answered 21/6 at 15:31 Comment(0)
P
-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();
});

Note: this.file should be you base64 file

Purity answered 12/2, 2014 at 13:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.