2

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);
Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778
Bruno
  • 131
  • 3
  • 17
  • Doesn't Jquery put some crap into the data object? What if you download the blob, eg. using [FileSaver.js](https://github.com/eligrey/FileSaver.js/) and try to open it in hex editor? – Tomáš Zato Oct 26 '15 at 10:58

2 Answers2

4

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

Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778
  • Thanks for your help! How do you force the download from server in a Web-Service? – Bruno Oct 26 '15 at 13:37
  • The header you're sending already forces it: `$res['Content-Disposition'] ='attachment; filename=' . basename($path);`. But most browsers have a plugin that overrides that. Here's the answer about it, ask in comments if you have problems: http://stackoverflow.com/a/3802630/607407 – Tomáš Zato Oct 26 '15 at 13:41
  • `r.responseType = "arraybuffer";` - did it for me! – User Jan 04 '18 at 10:46
0

It's hard to say without testing it but it looks like you are being returned the actual PDF, not a stream. Try just opening it, something like this:

window.open("/documents/docDownload");
Stephen Brickner
  • 2,584
  • 1
  • 11
  • 19
  • The service would have to return it as a response stream and not the actual PDF. Really you don't need that just for authentication, just use authentication to allow the download with the [Authorize] attribute on your service endpoint. – Stephen Brickner Oct 26 '15 at 10:37
  • I don't know php but I can see you are returning the response with a content type of PDF. If you want to return the raw data just return the byte array itself. Also, it appears you are pointing to an actual PDF not a DB field with the byte info. You would need something like ABC PDF to convert it back to a byte array. – Stephen Brickner Oct 26 '15 at 10:40