Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Binary XHR result to File blob - Jquery

Web service to donwload a document returns the following:

%PDF-1.5
%����
1 0 obj
<</Type/Catalog/Pages 2 0 R/Lang(en-GB) /StructTreeRoot 14 0 R/MarkInfo<</Marked true>>>>
endobj
2 0 obj
<</Type/Pages/Count 1/Kids[ 3 0 R] >>
endobj
3 0 obj
<</Type/Page/Parent 2 0 R/Resources<</Font<</F1 5 0 R/F2 8 0 R>>/ExtGState<</GS7 7 0 R>>/ProcSet[/PDF
/Text/ImageB/ImageC/ImageI] >>/MediaBox[ 0 0 595.32 841.92] /Contents 4 0 R/Group<</Type/Group/S/Transparency
/CS/DeviceRGB>>/Tabs/S/StructParents 0>>
endobj
4 0 obj

To convert this data to file, i'm using the following code:

$.ajax({
      url: "/documents/docDownload",
      type: "GET",
      async:   true,
      headers: {
        responseType: "arraybuffer"
      },
      success: function(data,status,xhr) {
        var file = new Blob([data], {type: 'application/pdf'});
        var fileURL = window.URL.createObjectURL(file);
        var a = document.createElement("a");
        a.href = fileURL;
        a.download = data.name || "newPDF2";
        document.body.appendChild(a);
        a.click();
        $(window).on('focus', function(e) {
          $('a').remove();
        });
      }
})

The functions returns me a pdf file, however when i open the pdf file, it's empty, seems that during the conversion was lost information.

Anybody knows why is it happening?

PHP - Web-Service code

$path = "/xxx/xxx/xxx/xxx/work.pdf";
$res = $app->response();
$res['Content-Description'] = 'File Transfer';
$res['Content-Type'] = 'application/force-download';
$res['Content-Type'] = 'application/pdf';
$res['Content-Disposition'] ='attachment; filename=' . basename($path);
$res['Content-Transfer-Encoding'] = 'binary';
$res['Expires'] = '0';
$res['Cache-Control'] = 'must-revalidate';
$res['Pragma'] = 'public';
$res['Content-Length'] = filesize($path);
readfile($path);
like image 777
Bruno Avatar asked Jan 21 '26 15:01

Bruno


1 Answers

So, it was just as I expected. You guys just won't learn - jQuery is not a good idea for complex XHR. They have bigger tasks to do than keep implementing rarely used XHR tweaks.

The problem was that as the received data was converted to string, it was converted to UTF-8, growing in length and becoming corrupted:

image description

We need to get a byte array, and I think that's what you were attempting with responseType: "arraybuffer". But that's not supposed to be a header. Server doesn't give a damn how you convert the received data. This is how you set it up:

var r = new XMLHttpRequest();
r.open("GET", ".../test.pdf");
// This configures how the data is parsed
r.responseType = "arraybuffer";
r.onload = function() {
  var file = new Blob([this.response], {type: 'application/pdf'});
  var fileURL = window.URL.createObjectURL(file);
  var a = document.createElement("a");
  a.href = fileURL;
  a.download = "newPDF2";
  document.body.appendChild(a);
  a.click();
  $(window).on('focus', function(e) {
    $(a).remove();
  });
}
r.send();

And this works. Also, if you want to set file name either parse it from url, like this:

var file_name = ".../test.pdf".match(/(^|\/)[^/]+$/)[2];

or send a proper file name header and parse it:

var file_name = this.getResponseHeader("Content-Disposition").match(/filename=(.*?)$/)[1];

However I still don't understand why don't you force download from server...

like image 122
Tomáš Zato - Reinstate Monica Avatar answered Jan 24 '26 06:01

Tomáš Zato - Reinstate Monica