Cómo mostrar blob (.pdf) en una aplicación AngularJS

106

He estado intentando mostrar un archivo pdf que obtengo como un blob de una $http.postrespuesta. El pdf debe mostrarse dentro de la aplicación usando <embed src>por ejemplo.

Me encontré con un par de publicaciones de pila pero de alguna manera mi ejemplo no parece funcionar.

JS:

De acuerdo con este documento , continué e intenté ...

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

Ahora, por lo que tengo entendido, fileURLcrea una URL temporal que el blog puede usar como referencia.

HTML:

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

No estoy seguro de cómo manejar esto en Angular, la situación ideal sería (1) asignarlo a un alcance, (2) 'preparar / reconstruir' el blob en un pdf (3) pasarlo al HTML usando <embed>porque yo desea mostrarlo dentro de la aplicación.

He estado investigando durante más de un día, pero de alguna manera parece que no puedo entender cómo funciona esto en Angular ... Y supongamos que las bibliotecas de visores de PDF no eran una opción.

Simo D'lo Mafuxwana
fuente
Hola D'lo DeProjuicer, ¿lograste solucionar tu problema de generar el PDF vía angular?
Raymond Nakampe
@michael D'lo DeProjuicer ¿Qué se debe hacer para el mismo caso en angular 2?
monica

Respuestas:

214

En primer lugar es necesario establecer la responseTypea arraybuffer. Esto es necesario si desea crear un blob de sus datos. Consulte Enviar_y_Recibir_datos_binarios . Entonces su código se verá así:

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

La siguiente parte es que debe usar el servicio $ sce para hacer que angular confíe en su URL. Esto se puede hacer de esta manera:

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

No olvide inyectar el servicio $ sce .

Si todo esto está hecho, ahora puede incrustar su pdf:

<embed ng-src="{{content}}" style="width:200px;height:200px;"></embed>
Miguel
fuente
9
Para mí, esto no funcionó en Chrome (35.0.1916.114 m). Resolvió esto usando <object> en lugar de <embed>: <object data = "{{content}}" type = "application / pdf"> </object>
HoffZ
2
Para mí (AngularJS 1.25) tuve que hacer: new Blob ([response.data]
Martin Connell
2
@HoffZ: Reemplacé el método de acceso directo $http.getcon uno completo, especificando el 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' }Y funciona :)
Nikolay Melnikov
1
Para mí, la única forma de hacerlo funcionar era crear el blob con en response.datalugar de response, así:var file = new Blob([response.data], {type: 'application/pdf'});
Alekos Filini
1
@ yosep-kim esto no funciona en IE debido a que el objeto URL no existe en IE: caniuse.com/#search=URL
Carlos
32

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

Servicios JS:

angular.module('xxxxxxxxApp')
    .factory('xxxxServicePDF', function ($http) {
        return {
            downloadPdf: function () {
            return $http.get('api/downloadPDF', { responseType: 'arraybuffer' }).then(function (response) {
                return response;
            });
        }
    };
});

Servicios web Java REST - 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
fuente
¿Qué versión de safari? window.URL es bueno en safari 9 y después: caniuse.com/#search=createObjectURL
Stéphane GRILLON
Probé y validé en mi MacBook pro y safari 9.0.2.
Stéphane GRILLON
Lo mismo, macBook el capitán. window.URL.createObjectURL (archivo); No sé dónde está el problema, pero el código no funciona. Puede ser que haga algo mal. Cualquiera gracias. No tengo tiempo para verificar qué no funciona y usar FileSaver.js
fdrv
si su solicitud está en línea, publique su URL por favor? ¿tienes el mismo Back-End?
Stéphane GRILLON
El atributo de descarga no es compatible con safari. caniuse.com/#search=download
Biswanath
21

Las sugerencias de Michael funcionan como un encanto para mí :) Si reemplaza $ http.post con $ http.get, recuerde que el método .get acepta 2 parámetros en lugar de 3 ... aquí es donde se pierde el tiempo ...;)

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

ver:

<object ng-show="content" data="{{content}}" type="application/pdf" style="width: 100%; height: 400px;"></object>
Jan Tchärmän
fuente
responseType:'arraybuffer', ¡me acaba de ahorrar un par de horas de sueño! +1
svarog
¿Cómo activar el guardado en lugar de imprimirlo en html?
fdrv
gracias, esto me salvó un par de horas, también se puede reemplazar $scope.content = $sce.trustAsResourceUrl(fileURL);con $window.open(fileURL, '_self', '');y abra el archivo en pantalla completa.
Tavitos
9

Enfrenté dificultades al usar "window.URL" con Opera Browser, ya que resultaría en "undefined". Además, con window.URL, el documento PDF nunca se abrió en Internet Explorer y Microsoft Edge (permanecería esperando para siempre). Se me ocurrió la siguiente solución que funciona en IE, Edge, Firefox, Chrome y Opera (no he probado con 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);
    }
}

¡Avísame si te ayudó! :)

Manuel Hernandez
fuente
Este enfoque no abre el documento PDF en la ventana del navegador en IE, pero solicita al usuario que lo descargue. ¿Hay alguna solución a esto?
sdd
1
El código anterior es para descargar el archivo PDF y dejar que su aplicación PDF Reader predeterminada se encargue de abrirlo. Incluso funciona bien en dispositivos móviles. La razón es que, aunque pude abrir el PDF en algunos navegadores, no pude abrirlo en otros. Así que pensé que era mejor tener una solución que se ejecutara en todos los navegadores (incluidos los navegadores móviles) para descargar el archivo PDF.
Manuel Hernandez
Puede utilizar el siguiente código para ver el PDF en una nueva pestaña: window.open (reader.result, '_blank');
samneric
6

Agregar responseType a la solicitud que se realiza desde angular es de hecho la solución, pero para mí no funcionó hasta que configuré responseType en blob , no en arrayBuffer. El código se explica por sí mismo:

    $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
fuente
2
en realidad, con el 'blob'tipo es posible escribir más corto: FileSaver.saveAs(response.data, getFileNameFromHttpResponse(response));No es necesario crearBlob
Alena Kastsiukavets
0

He luchado durante los últimos días tratando de descargar archivos PDF e imágenes, todo lo que pude descargar fueron archivos de texto simples.

La mayoría de las preguntas tienen los mismos componentes, pero tomó un tiempo averiguar el orden correcto para que funcionara.

Gracias @Nikolay Melnikov, su comentario / respuesta a esta pregunta fue lo que hizo que funcionara.

En pocas palabras, aquí está mi llamada de backend del servicio 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'
    });
  }

Desde mi 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
fuente
0

Respuesta más reciente (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
fuente
-1

Una sugerencia de código que acabo de usar en mi proyecto 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
fuente
1
por favor agregue algo breve también.
Farhana