114

I have been trying to display pdf file which I am getting as a blob from a $http.post response. The pdf must be displayed within the app using <embed src> for example.

I came across a couple of stack posts but somehow my example doesn't seem to work.

JS:

According to this doc, I went on and tried...

$http.post('/postUrlHere',{myParams}).success(function (response) {
 var file = new Blob([response], {type: 'application/pdf'});
 var fileURL = URL.createObjectURL(file);
 $scope.content = fileURL;
});

Now from what I understand, fileURL creates a temporary URL that the blog can use as a reference.

HTML:

<embed src="{{content}}" width="200" height="200"></embed>

I am not sure how to handle this in Angular, the ideal situation would be to (1) assign it to a scope, (2) 'prepare/rebuild' the blob to a pdf (3) pass it to the HTML using <embed> because I want to display it within the app.

I have been researching for more than a day now but somehow I can't seem to understand how this works in Angular... And let's just assume the pdf viewer libraries out there weren't an option.

Gil Epshtain
  • 8,670
  • 7
  • 63
  • 89
Simo Mafuxwana
  • 3,702
  • 6
  • 41
  • 59

8 Answers8

222

First of all you need to set the responseType to arraybuffer. This is required if you want to create a blob of your data. See Sending_and_Receiving_Binary_Data. So your code will look like this:

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

The next part is, you need to use the $sce service to make angular trust your url. This can be done in this way:

$scope.content = $sce.trustAsResourceUrl(fileURL);

Do not forget to inject the $sce service.

If this is all done you can now embed your pdf:

<embed ng-src="{{content}}" style="width:200px;height:200px;"></embed>
michael
  • 16,221
  • 7
  • 55
  • 60
  • 9
    For me this did not work in Chrome (35.0.1916.114 m). Solved this by using instead of : – HoffZ Jun 16 '14 at 13:34
  • Hi Michael and HoffZ, which js libraries did you include to have your PDF generated as I am experiencing problems about the file(PDF) being corrupt, it's generated but doesn't display in my case. – Raymond Nakampe Sep 04 '14 at 10:18
  • 2
    For me (AngularJS 1.25) I had to do: new Blob([response.data] – Martin Connell Oct 07 '14 at 18:57
  • 2
    @HoffZ: I replaced the shortcut method `$http.get` with a full one, specifying the `responseType` field: ```{ url: "http://127.0.0.1:8080/resources/jobs/af471106-2e71-4fe6-946c-cd1809c659e5/result/?key="+$scope.key, method: "GET", headers: { 'Accept': 'application/pdf' }, responseType: 'arraybuffer' }``` And it works :) – Nikolay Melnikov Oct 25 '14 at 23:18
  • Not sure why, since @michael follows the [specification](https://docs.angularjs.org/api/ng/service/$http). The only difference in my case: I use `GET`, not post. [Source](http://stackoverflow.com/questions/22447952/angularjs-http-post-convert-binary-to-excel-file-and-download) for my solution. – Nikolay Melnikov Oct 25 '14 at 23:25
  • Using ng-src didn't work with the embed tag when you change the src in the controller, so it loads first time but if you change the src because of a click for example then the src in the embed tag doesn't change. It is working fine with object tag though – Sul Aga Sep 07 '15 at 09:43
  • hi, you solutions worked like charm...is there a way to remove PDF toolbar...i tried toolbar=0 navpanes=0 scrollbar =0 but not working – Anil D Jan 06 '16 at 13:38
  • 1
    For me the only way to make it work was to create the blob with `response.data` instead of `response`, like this: `var file = new Blob([response.data], {type: 'application/pdf'});` – Alekos Filini Sep 26 '16 at 09:50
  • or if you like it opened in new windows ` $window.open($sce.trustAsResourceUrl(fileURL)); ` – oCcSking Jan 05 '17 at 09:58
  • 1
    @yosep-kim this does not work on IE because of URL object does not exist in IE: http://caniuse.com/#search=URL – Carlos Mar 20 '17 at 16:15
  • Hi Friends, $http.get("xx.pdf", {responseType:'arraybuffer'}) .success(function (response) { var file = new Blob([(response)], {type: 'application/pdf'}); var fileURL = URL.createObjectURL(file); $scope.content=$sce.trustAsResourceUrl(fileURL); }); the above one is working for me. But, When i pass converted array byte like document to pdf array byte which is coming in response instead of xx.pdf file. The pdf is loaded with out content. i can see the page count as 15. Can you help me on this issue like how to show the content. – Raphael Apr 12 '17 at 08:59
  • You can simply use {responseType:'arraybuffer'} when you make a request, and then location.href = fileURL. – Daniele Aug 01 '17 at 08:22
34

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('xxxxController', function ($scope, xxxxServicePDF) {
        $scope.downloadPdf = function () {
            var fileName = "test.pdf";
            var a = document.createElement("a");
            document.body.appendChild(a);
            a.style = "display: none";
            xxxxServicePDF.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('xxxxServicePDF', 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;
    }
Stéphane GRILLON
  • 11,140
  • 10
  • 85
  • 154
  • which version of safari? window.URL is good in safari 9 and after: http://caniuse.com/#search=createObjectURL – Stéphane GRILLON Mar 30 '16 at 19:54
  • I tested and valided on my MacBook pro and safari 9.0.2. – Stéphane GRILLON Mar 30 '16 at 20:14
  • Same, macBook el captain. window.URL.createObjectURL(file); i dont where is the problem but code doesnt work. May be i do something wrong. Any thank you. I haven't time to check what it not working and use FileSaver.js – fdrv Mar 30 '16 at 21:26
  • if your application is online, post your URL please? have you the same Back-End? – Stéphane GRILLON Mar 31 '16 at 06:33
  • download attribute is not supported in safari. http://caniuse.com/#search=download – Biswanath Sep 27 '16 at 11:14
  • @Biswanath, which version of safari? window.URL is good in safari 9 and after: caniuse.com/#search=createObjectURL – Stéphane GRILLON Sep 27 '16 at 14:06
  • @sgrillon: I was mentioning the download attribute http://caniuse.com/#search=download – Biswanath Sep 27 '16 at 14:13
  • in api/downloadPDF - > which is containing only bytearray. if bytearray, it is not working for me. if i give local file as x.pdf. then my pdf is rendering on page. i dont want to download. i need to show the pdf file in the page itself using angular. can you give your solutions for showing pdf file in view – Raphael Apr 20 '17 at 05:58
  • if you put "application/pdf" in a header, your pdf is open in tab browser. in java => headers.setContentType(MediaType.parseMediaType("application/pdf")); – Stéphane GRILLON Apr 20 '17 at 18:56
23

michael's suggestions works like a charm for me :) If you replace $http.post with $http.get, remember that the .get method accepts 2 parameters instead of 3... this is where is wasted my time... ;)

controller:

$http.get('/getdoc/' + $stateParams.id,     
{responseType:'arraybuffer'})
  .success(function (response) {
     var file = new Blob([(response)], {type: 'application/pdf'});
     var fileURL = URL.createObjectURL(file);
     $scope.content = $sce.trustAsResourceUrl(fileURL);
});

view:

<object ng-show="content" data="{{content}}" type="application/pdf" style="width: 100%; height: 400px;"></object>
fabien7474
  • 16,300
  • 22
  • 96
  • 124
Jan Tchärmän
  • 957
  • 1
  • 9
  • 13
  • 1
    `responseType:'arraybuffer'`, just saved me a couple of sleeping hours! +1 – svarog Jan 04 '16 at 22:06
  • how to trigger the save instead to print it in html? – fdrv Mar 30 '16 at 17:33
  • thank you, this saved me a couple of hours, you can also replace `$scope.content = $sce.trustAsResourceUrl(fileURL);` with `$window.open(fileURL, '_self', '');` and open the file on fullscreen. – Tavitos Aug 25 '16 at 15:41
10

I faced difficulties using "window.URL" with Opera Browser as it would result to "undefined". Also, with window.URL, the PDF document never opened in Internet Explorer and Microsoft Edge (it would remain waiting forever). I came up with the following solution that works in IE, Edge, Firefox, Chrome and Opera (have not tested with Safari):

$http.post(postUrl, data, {responseType: 'arraybuffer'})
.success(success).error(failed);

function success(data) {
   openPDF(data.data, "myPDFdoc.pdf");
};

function failed(error) {...};

function openPDF(resData, fileName) {
    var ieEDGE = navigator.userAgent.match(/Edge/g);
    var ie = navigator.userAgent.match(/.NET/g); // IE 11+
    var oldIE = navigator.userAgent.match(/MSIE/g); 

    var blob = new window.Blob([resData], { type: 'application/pdf' });

    if (ie || oldIE || ieEDGE) {
       window.navigator.msSaveBlob(blob, fileName);
    }
    else {
       var reader = new window.FileReader();
       reader.onloadend = function () {
          window.location.href = reader.result;
       };
       reader.readAsDataURL(blob);
    }
}

Let me know if it helped! :)

Manuel Hernandez
  • 581
  • 1
  • 7
  • 11
  • This approach does not open the PDF document in the browser window in IE but prompts the user to download it. Is there any work around this? – sdd Sep 07 '16 at 23:28
  • 1
    The above code is to download the PDF file and let your default PDF Reader app take over to open it. It even works well on mobile devices. Reason is, while I was able to open the PDF on some browsers, I could not open it on other ones. So I thought it was best to have a solution that would run on all browsers (including mobile browsers) to download the PDF file. – Manuel Hernandez Sep 28 '16 at 20:22
  • You can use the following code to view the PDF in a new tab: window.open(reader.result, '_blank'); – samneric Nov 09 '16 at 21:54
6

Adding responseType to the request that is made from angular is indeed the solution, but for me it didn't work until I've set responseType to blob, not to arrayBuffer. The code is self explanatory:

    $http({
            method : 'GET',
            url : 'api/paperAttachments/download/' + id,
            responseType: "blob"
        }).then(function successCallback(response) {
            console.log(response);
             var blob = new Blob([response.data]);
             FileSaver.saveAs(blob, getFileNameFromHttpResponse(response));
        }, function errorCallback(response) {   
        });
ancab
  • 780
  • 1
  • 7
  • 12
  • 2
    actually, with `'blob'` type it is possible to write shorter: `FileSaver.saveAs(response.data, getFileNameFromHttpResponse(response));` No need to create `Blob` – Alena Kastsiukavets Jul 19 '17 at 14:14
1

Most recent answer (for Angular 8+):

this.http.post("your-url",params,{responseType:'arraybuffer' as 'json'}).subscribe(
  (res) => {
    this.showpdf(res);
  }
)};

public Content:SafeResourceUrl;
showpdf(response:ArrayBuffer) {
  var file = new Blob([response], {type: 'application/pdf'});
  var fileURL = URL.createObjectURL(file);
  this.Content = this.sanitizer.bypassSecurityTrustResourceUrl(fileURL);
}

  HTML :

  <embed [src]="Content" style="width:200px;height:200px;" type="application/pdf" />
ashishpm
  • 408
  • 1
  • 6
  • 19
0

I have struggled for the past couple of days trying to download pdfs and images,all I was able to download was simple text files.

Most of the questions have the same components, but it took a while to figure out the right order to make it work.

Thank you @Nikolay Melnikov, your comment/reply to this question was what made it work.

In a nutshell, here is my AngularJS Service backend call:

  getDownloadUrl(fileID){
    //
    //Get the download url of the file
    let fullPath = this.paths.downloadServerURL + fileId;
    //
    // return the file as arraybuffer 
    return this.$http.get(fullPath, {
      headers: {
        'Authorization': 'Bearer ' + this.sessionService.getToken()
      },
      responseType: 'arraybuffer'
    });
  }

From my controller:

downloadFile(){
   myService.getDownloadUrl(idOfTheFile).then( (response) => {
      //Create a new blob object
      let myBlobObject=new Blob([response.data],{ type:'application/pdf'});

      //Ideally the mime type can change based on the file extension
      //let myBlobObject=new Blob([response.data],{ type: mimeType});

      var url = window.URL || window.webkitURL
      var fileURL = url.createObjectURL(myBlobObject);
      var downloadLink = angular.element('<a></a>');
      downloadLink.attr('href',fileURL);
      downloadLink.attr('download',this.myFilesObj[documentId].name);
      downloadLink.attr('target','_self');
      downloadLink[0].click();//call click function
      url.revokeObjectURL(fileURL);//revoke the object from URL
    });
}
Javier Carbajal
  • 111
  • 1
  • 2
-2

A suggestion of code that I just used in my project using AngularJS v1.7.2

$http.get('LabelsPDF?ids=' + ids, { responseType: 'arraybuffer' })
            .then(function (response) {
                var file = new Blob([response.data], { type: 'application/pdf' });
                var fileURL = URL.createObjectURL(file);
                $scope.ContentPDF = $sce.trustAsResourceUrl(fileURL);
            });

<embed ng-src="{{ContentPDF}}" type="application/pdf" class="col-xs-12" style="height:100px; text-align:center;" />
Fernando Magno
  • 375
  • 6
  • 15