Como exibir blob (.pdf) em um aplicativo AngularJS

106

Tenho tentado exibir o arquivo PDF que estou obtendo como um blob de uma $http.postresposta. O pdf deve ser exibido dentro do aplicativo usando<embed src> por exemplo.

Eu me deparei com alguns posts, mas de alguma forma meu exemplo não parece funcionar.

JS:

De acordo com este doc , continuei e tentei ...

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

Agora, pelo que entendi, fileURL cria um URL temporário que o blog pode usar como referência.

HTML:

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

Não tenho certeza de como lidar com isso no Angular, a situação ideal seria (1) atribuí-lo a um escopo, (2) 'preparar / reconstruir' o blob para um pdf (3) passá-lo para o HTML usando<embed> porque eu deseja exibi-lo no aplicativo.

Venho pesquisando há mais de um dia, mas de alguma forma não consigo entender como isso funciona no Angular ... E vamos apenas assumir que as bibliotecas do visualizador de PDF por aí não eram uma opção.

Simo D'lo Mafuxwana
fonte
Olá D'lo DeProjuicer, você conseguiu consertar seu problema de geração de PDF via angular?
Raymond Nakampe
@michael D'lo DeProjuicer O que deve ser feito para o mesmo gabinete no angular 2?
monica

Respostas:

214

Primeiro de tudo, você precisa definir o responseTypeparaarraybuffer . Isso é necessário se você deseja criar um blob de seus dados. Consulte Sending_and_Receiving_Binary_Data . Portanto, seu código será semelhante a este:

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

A próxima parte é, você precisa usar o serviço $ sce para tornar o angular trust seu url. Isso pode ser feito desta forma:

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

Não se esqueça de injetar o serviço $ sce .

Se tudo isso estiver feito, agora você pode incorporar seu pdf:

<embed ng-src="{{content}}" style="width:200px;height:200px;"></embed>
Michael
fonte
9
Para mim, isso não funcionou no Chrome (35.0.1916.114 m). Resolvido isso usando <object> em vez de <embed>: <object data = "{{content}}" type = "application / pdf"> </object>
HoffZ
2
Para mim (AngularJS 1.25) eu tinha que fazer: novo Blob ([response.data]
Martin Connell
2
@HoffZ: Substituí o método de atalho $http.getpor um completo, especificando o responseTypecampo: { 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' }E funciona :)
Nikolay Melnikov
1
Para mim, a única maneira de fazer funcionar era criar o blob com, em response.datavez de response, assim:var file = new Blob([response.data], {type: 'application/pdf'});
Alekos Filini
1
@ yosep-kim isto não funciona no IE porque o objeto URL não existe no IE: caniuse.com/#search=URL
Carlos
32

Eu uso AngularJS v1.3.4

HTML:

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

Controlador JS:

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

Serviços JS:

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
fonte
qual versão do safari? window.URL é bom no safari 9 e depois: caniuse.com/#search=createObjectURL
Stéphane GRILLON
Testei e validei no meu MacBook pro e safari 9.0.2.
Stéphane GRILLON
O mesmo, macBook el capitão. window.URL.createObjectURL (arquivo); Eu não onde está o problema, mas o código não funciona. Pode ser que eu faça algo errado. Obrigado. Não tenho tempo para verificar o que não está funcionando e usar FileSaver.js
fdrv
se seu aplicativo estiver online, poste seu URL, por favor? você tem o mesmo back-end?
Stéphane GRILLON
O atributo de download não é compatível com o Safari. caniuse.com/#search=download
Biswanath
21

as sugestões de Michael funcionam perfeitamente para mim :) Se você substituir $ http.post por $ http.get, lembre-se de que o método .get aceita 2 parâmetros em vez de 3 ... é aqui que perco meu tempo ...;)

controlador:

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

Visão:

<object ng-show="content" data="{{content}}" type="application/pdf" style="width: 100%; height: 400px;"></object>
Jan Tchärmän
fonte
responseType:'arraybuffer', só me salvou algumas horas de sono! +1
svarog
como acionar o save em vez de imprimi-lo em html?
fdrv
obrigado, isso me salvou um par de horas, você também pode substituir $scope.content = $sce.trustAsResourceUrl(fileURL);com $window.open(fileURL, '_self', '');e abra o arquivo em tela cheia.
Tavitos
9

Tive dificuldades em usar "window.URL" com o navegador Opera, pois resultaria em "indefinido". Além disso, com window.URL, o documento PDF nunca foi aberto no Internet Explorer e no Microsoft Edge (ele ficaria esperando para sempre). Eu vim com a seguinte solução que funciona no IE, Edge, Firefox, Chrome e Opera (não testei com 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);
    }
}

Deixe-me saber se ajudou! :)

Manuel Hernandez
fonte
Essa abordagem não abre o documento PDF na janela do navegador no IE, mas solicita que o usuário faça o download. Existe alguma solução para isso?
sdd
1
O código acima é para baixar o arquivo PDF e permitir que seu aplicativo PDF Reader padrão assuma o controle para abri-lo. Até funciona bem em dispositivos móveis. A razão é que, embora eu conseguisse abrir o PDF em alguns navegadores, não consegui abri-lo em outros. Portanto, achei melhor ter uma solução que rodasse em todos os navegadores (incluindo navegadores móveis) para baixar o arquivo PDF.
Manuel Hernandez
Você pode usar o seguinte código para visualizar o PDF em uma nova guia: window.open (reader.result, '_blank');
samneric
6

Adicionar responseType à solicitação feita a partir do angular é de fato a solução, mas para mim não funcionou até que eu defina responseType como blob , não como arrayBuffer. O código é autoexplicativo:

    $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
fonte
2
na verdade, com 'blob'tipo é possível escrever mais curto: FileSaver.saveAs(response.data, getFileNameFromHttpResponse(response));Não há necessidade de criarBlob
Alena Kastsiukavets
0

Eu tenho lutado nos últimos dias tentando fazer download de PDFs e imagens, tudo o que consegui fazer foram arquivos de texto simples.

A maioria das perguntas tem os mesmos componentes, mas demorou um pouco para descobrir a ordem certa para que funcionasse.

Obrigado @Nikolay Melnikov, seu comentário / resposta a esta pergunta foi o que fez funcionar.

Em poucas palavras, aqui está minha chamada de back-end do serviço AngularJS:

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

Do meu controlador:

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

Resposta mais recente (para 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
fonte
-1

Uma sugestão de código que acabei de usar em meu projeto usando 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
fonte
1
por favor, adicione alguns resumos também.
Farhana