0

Trying to download a pdf doc with Angular, we wrote this:

var _config = { headers : {'Accept' : '*/*'}, responseType : 'arraybuffer' }; var success = function(data, status, header, config) { $log.debug('Download resume success - type:' + typeof (data)); var _contentType = (header('Content-Type')); var blob = new Blob([ data ], { type : _contentType }); var url = (window.URL || window.webkitURL).createObjectURL(blob); var anchor = angular.element('<a/>'); anchor.attr({ href : url, target : '_blank', download : _fileName })[0].click(); } $http.get(_url, _config).success(success).error(error); 

We've tried all permutations of blob and arraybuffer but data always returns as a String with the extended characters 'decoded' which is to say broken.

_contentType is always application/pdf although we've tried forcing it to application/octet-stream as well.

Suggestions and pointers welcomed!

Update

This looks to be a bug someplace between Angular (1.3.15 & 1.4.8) and Chrome's (46.0) XMLHttpRequest implementation where response.data is always returned as a 'decoded' string. Neither Firefox (42) nor IE (10) have this problem and all of the solutions below would most likely work (as does or original solution).

I've reported this as a possible bug to both AngularJS & Chrome.

4
  • Here's a guy who did it. stackoverflow.com/a/23683763/652728 Commented Nov 23, 2015 at 20:41
  • Yeah, why doesn't this work in straight-up Angular? Seems like it should. Commented Nov 23, 2015 at 20:44
  • Any reason you create a link instead of just window.location() it? Commented Nov 23, 2015 at 20:56
  • No. Right now we're concentrating on getting the file to the client with the content intact. Commented Nov 23, 2015 at 21:09

4 Answers 4

3

I ran in to this problem myself a few months back. I had to use FileSaver.js to handle it.

So after you install FileSaver.js have your success function look something like this:

function success(response) { var blob = new Blob([response.data], { type: "application/pdf"}); //change download.pdf to the name of whatever you want your file to be saveAs(blob, "download.pdf"); } 
Sign up to request clarification or add additional context in comments.

10 Comments

We've tried FileSaver.js, no difference. One question I have with FileSaver is how do I know the Blob is handled by FileSaver and not by the built-in Blob?
What are you using in the backend? I also had problems with my node/express server corrupting my zip data. I had to fs.writeFile to a temporary file on the server then res.download(pathOfFile, filename + ".zip"); And that seemed to work.
Spring is our backend. The file is only modified when it gets to Angular, when we inspect the data object. Over the wire it's fine. GET from another Spring server is fine. GET from Postman saves the file to disk and it's uncorrupted. The only time we have trouble is trying to get it download to our Angular client :-(
What version of Angular are you using? Have you logged the data from the response to make sure the data is what you're expecting it to be? And lastly, are you making sure that the MIME type is set to "application/pdf"? I also noticed you're using .success to handle your http promise, which has been deprecated, which is why I'm asking which version you're using.
1.3.x, we've tried 1.4.x and no difference (although we're going to upgrade everything to 1.4.x). Yes, over the wire the data is a pdf file, save it to disk and Acrobat opens it without a problem. Yes, the mime type is always application/pdf unless we force it to octet-stream.
|
1

You need to tell angularjs that you are receiving a binary file in your $http.get or $http.post method. Use {responseType : 'arraybuffer'}.

For example:

$http.post('/pdf/link', requestData, { responseType : 'arraybuffer' }).then(function (data, status, headers) { headers = data.headers(); var contentType = headers['content-type']; var linkElement = document.createElement('a'); try { var blob = new Blob([data.data], {type: "application/pdf"}); var url = window.URL.createObjectURL(blob); linkElement.setAttribute('href', url); linkElement.setAttribute("download", "mypdf.pdf"); var clickEvent = new MouseEvent("click", { "view": window, "bubbles": true, "cancelable": false }); linkElement.dispatchEvent(clickEvent); } catch (ex) { console.log(ex); } } 

1 Comment

This can be improved by the following lines: linkElement.href = url; linkElement.download = 'file.pdf'; linkElement.click();
0

Just change it from your html:

<a target="_self" href="example.com/uploads/file.pdf" download="file_new_name.pdf"> 

3 Comments

That would work if the file wasn't behind a secure REST API.
what if you set the header: $http.defaults.headers.common.Authorization = 'Basic YmVlcDpib29w';
No, it's much more complicated than that. We're integrated with Spring Security.
0

Check encoding with your api and in this lines, this worked for me:

 var filepdf = Base64.encode(response.data); $scope.filepdf = 'data:application/pdf;base64,' + pdfdata; 

CONTROLLER

 $scope.pdfDownloads = function() { DownloadPath.get({id: $rootScope.user.account_id}, function (data) { $scope.createtext = "Download PDF file"; $scope.download_pdf_filename = data.filename; $scope.getPdf = true; $scope.download_dir = file_url; $http.get($scope.download_dir).then(function (response) { var pdfdata= Base64.encode(response.data); $scope.pdfdata= 'data:application/pdf;base64,' + pdfdata; $('#pdfanchor').attr({ href: $scope.pdfdata, download: $scope.download_pdf_filename }) }); }); }; 

HTML

<a href=""ng-click="pdfDownloads()" >Download</a> 

EDIT: THIS IS HOW WE STRUCTURED THE RESPONSE (PHP functions)

contentType = 'application/octet-stream'; contentDescription = 'File Transfer'; contentDisposition = 'attachment; filename=' . basename(file_nae); expires = 0; cacheControl = 'must-revalidate'; contentLength = filesize($file_name); body = base64_encode(file_get_contents($file_name)); 

4 Comments

That would work if we base64 encoded the file before we sent it, but we don't. The file is a raw (binary) pdf file.
But that can be done right? just encode before response?? :|
Honestly this was a hussle for me, in the end we decided to change the api response.... not ideal
thanks for the answer, hope you solve it... will check back, cheers

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.