page change angular typescript download fileapi

change - page title angular 4



¿Cómo descargo un archivo con Angular2? (23)

Tengo una aplicación WebApi / MVC para la que estoy desarrollando un cliente angular2 (para reemplazar MVC). Tengo problemas para entender cómo Angular guarda un archivo.

La solicitud está bien (funciona bien con MVC, y podemos registrar los datos recibidos) pero no puedo entender cómo guardar los datos descargados (principalmente sigo la misma lógica que en esta publicación ). Estoy seguro de que es estúpidamente simple, pero hasta ahora simplemente no lo entiendo.

El código de la función componente está debajo. He intentado diferentes alternativas, la forma de blob debería ser la forma de ir tan lejos como lo entendí, pero no hay una función createObjectURL en URL . Ni siquiera puedo encontrar la definición de URL en la ventana, pero aparentemente existe. Si uso el módulo FileSaver.js obtengo el mismo error. Así que supongo que esto es algo que cambió recientemente o aún no se ha implementado. ¿Cómo puedo activar el archivo guardado en A2?

downloadfile(type: string){ let thefile = {}; this.pservice.downloadfile(this.rundata.name, type) .subscribe(data => thefile = new Blob([data], { type: "application/octet-stream" }), //console.log(data), error => console.log("Error downloading the file."), () => console.log(''Completed file download.'')); let url = window.URL.createObjectURL(thefile); window.open(url); }

En aras de la exhaustividad, el servicio que recupera los datos está debajo, pero lo único que hace es emitir la solicitud y pasar los datos sin mapear si tiene éxito:

downloadfile(runname: string, type: string){ return this.authHttp.get( this.files_api + this.title +"/"+ runname + "/?file="+ type) .catch(this.logAndPassOn); }


¿Qué tal esto?

this.http.get(targetUrl,{responseType:ResponseContentType.Blob}) .catch((err)=>{return [do yourself]}) .subscribe((res:Response)=>{ var a = document.createElement("a"); a.href = URL.createObjectURL(res.blob()); a.download = fileName; // start download a.click(); })

Yo podría hacer con eso.
No necesita paquete adicional.


Actualice la respuesta de Héctor utilizando el protector de archivos y HttpClient para el paso 2:

public downloadFile(file: File): Observable<Blob> { return this.http.get(file.fullPath, {responseType: ''blob''}) }


Aquí hay algo que hice en mi caso:

// service method downloadFiles(vendorName, fileName) { return this.http.get(this.appconstants.filesDownloadUrl, { params: { vendorName: vendorName, fileName: fileName }, responseType: ''arraybuffer'' }).map((res: ArrayBuffer) => { return res; }) .catch((error: any) => _throw(''Server error: '' + error)); } // a controller function which actually downloads the file saveData(data, fileName) { var a = document.createElement("a"); document.body.appendChild(a); a.style = "display: none"; let blob = new Blob([data], { type: "octet/stream" }), url = window.URL.createObjectURL(blob); a.href = url; a.download = fileName; a.click(); window.URL.revokeObjectURL(url); } // a controller function to be called on requesting a download downloadFiles() { this.service.downloadFiles(this.vendorName, this.fileName).subscribe(data => this.saveData(data, this.fileName), error => console.log("Error downloading the file."), () => console.info("OK")); }

Se hace referencia a la solución desde here


Como mencionó Alejandro Corredor , es un simple error de alcance. La subscribe se ejecuta de forma asincrónica y la open debe colocarse en ese contexto, de modo que los datos terminen de cargarse cuando activemos la descarga.

Dicho esto, hay dos formas de hacerlo. Como los documentos recomiendan, el servicio se encarga de obtener y asignar los datos:

//On the service: downloadfile(runname: string, type: string){ var headers = new Headers(); headers.append(''responseType'', ''arraybuffer''); return this.authHttp.get( this.files_api + this.title +"/"+ runname + "/?file="+ type) .map(res => new Blob([res],{ type: ''application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'' })) .catch(this.logAndPassOn); }

Luego, en el componente solo nos suscribimos y tratamos con los datos mapeados. Hay dos posibilidades El primero , como se sugiere en la publicación original, pero necesita una pequeña corrección como señaló Alejandro:

//On the component downloadfile(type: string){ this.pservice.downloadfile(this.rundata.name, type) .subscribe(data => window.open(window.URL.createObjectURL(data)), error => console.log("Error downloading the file."), () => console.log(''Completed file download.'')); }

La segunda forma sería usar FileReader. La lógica es la misma, pero podemos esperar explícitamente a que FileReader cargue los datos, evitando el anidamiento y resolviendo el problema asíncrono.

//On the component using FileReader downloadfile(type: string){ var reader = new FileReader(); this.pservice.downloadfile(this.rundata.name, type) .subscribe(res => reader.readAsDataURL(res), error => console.log("Error downloading the file."), () => console.log(''Completed file download.'')); reader.onloadend = function (e) { window.open(reader.result, ''Excel'', ''width=20,height=10,toolbar=0,menubar=0,scrollbars=no''); } }

Nota: Estoy tratando de descargar un archivo de Excel, y aunque la descarga se activa (por lo que esto responde a la pregunta), el archivo está dañado. Vea la respuesta a esta publicación para evitar el archivo corrupto.


Comparto la solución que me ayudó (cualquier mejora es muy apreciada)

En su servicio ''pservice'':

getMyFileFromBackend(typeName: string): Observable<any>{ let param = new URLSearchParams(); param.set(''type'', typeName); // setting ''responseType: 2'' tells angular that you are loading an arraybuffer return this.http.get(http://MYSITE/API/FILEIMPORT, {search: params, responseType: 2}) .map(res => res.text()) .catch((error:any) => Observable.throw(error || ''Server error'')); }

Parte componente :

downloadfile(type: string){ this.pservice.getMyFileFromBackend(typename).subscribe( res => this.extractData(res), (error:any) => Observable.throw(error || ''Server error'') ); } extractData(res: string){ // transforme response to blob let myBlob: Blob = new Blob([res], {type: ''application/vnd.oasis.opendocument.spreadsheet''}); // replace the type by whatever type is your response var fileURL = URL.createObjectURL(myBlob); // Cross your fingers at this point and pray whatever you''re used to pray window.open(fileURL); }

En la parte componente, llama al servicio sin suscribirse a una respuesta. La suscripción para obtener una lista completa de los tipos de mimo de openOffice, consulte: http://www.openoffice.org/framework/documentation/mimetypes/mimetypes.html


Descargue la solución * .zip para angular 2.4.x: debe importar ResponseContentType de ''@ angular / http'' y cambiar responseType a ResponseContentType.ArrayBuffer (de forma predeterminada, ResponseContentType.Json)

getZip(path: string, params: URLSearchParams = new URLSearchParams()): Observable<any> { let headers = this.setHeaders({ ''Content-Type'': ''application/zip'', ''Accept'': ''application/zip'' }); return this.http.get(`${environment.apiUrl}${path}`, { headers: headers, search: params, responseType: ResponseContentType.ArrayBuffer //magic }) .catch(this.formatErrors) .map((res:Response) => res[''_body'']); }


El problema es que el observable se ejecuta en otro contexto, por lo que cuando intenta crear la URL var, tiene un objeto vacío y no el blob que desea.

Una de las muchas formas que existen para resolver esto es la siguiente:

this._reportService.getReport().subscribe(data => this.downloadFile(data)),//console.log(data), error => console.log(''Error downloading the file.''), () => console.info(''OK'');

Cuando la solicitud esté lista, llamará a la función "downloadFile" que se define de la siguiente manera:

downloadFile(data: Response) { const blob = new Blob([data], { type: ''text/csv'' }); const url= window.URL.createObjectURL(blob); window.open(url); }

el blob se ha creado perfectamente y, por lo tanto, la URL var, si no abre la nueva ventana, compruebe que ya ha importado ''rxjs / Rx'';

import ''rxjs/Rx'' ;

Espero que esto pueda ayudarte.


Enfrenté este mismo caso hoy, tuve que descargar un archivo pdf como archivo adjunto (el archivo no debería mostrarse en el navegador, sino descargarse). Para lograr eso, descubrí que tenía que obtener el archivo en un Blob angular y, al mismo tiempo, agregar un encabezado de Content-Disposition en la respuesta.

Esto fue lo más simple que pude obtener (Angular 7):

Dentro del servicio:

getFile(id: String): Observable<HttpResponse<Blob>> { return this.http.get(`./file/${id}`, {responseType: ''blob'', observe: ''response''}); }

Luego, cuando necesito descargar el archivo en un componente, simplemente puedo:

fileService.getFile(''123'').subscribe((file: HttpResponse<Blob>) => window.location.href = file.url);

ACTUALIZAR:

Se eliminó la configuración de encabezado innecesaria del servicio


Esto es para personas que buscan cómo hacerlo usando HttpClient y File-Saver:

  1. Instalar protector de archivos

npm install file-saver --save

npm install @ types / file-saver --save

Clase de servicio API:

export() { return this.http.get(this.download_endpoint, {responseType: ''blob''}); }

Componente:

import { saveAs } from ''file-saver''; exportPdf() { this.api_service.export().subscribe(data => saveAs(data, `pdf report.pdf`)); }


Estoy usando Angular 4 con el objeto 4.3 httpClient. Modifiqué una respuesta que encontré en el Blog técnico de Js que crea un objeto de enlace, lo usa para realizar la descarga y luego lo destruye.

Cliente:

doDownload(id: number, contentType: string) { return this.http .get(this.downloadUrl + id.toString(), { headers: new HttpHeaders().append(''Content-Type'', contentType), responseType: ''blob'', observe: ''body'' }) } downloadFile(id: number, contentType: string, filename:string) { return this.doDownload(id, contentType).subscribe( res => { var url = window.URL.createObjectURL(res); var a = document.createElement(''a''); document.body.appendChild(a); a.setAttribute(''style'', ''display: none''); a.href = url; a.download = filename; a.click(); window.URL.revokeObjectURL(url); a.remove(); // remove the element }, error => { console.log(''download error:'', JSON.stringify(error)); }, () => { console.log(''Completed file download.'') }); }

El valor de this.downloadUrl se ha configurado previamente para que apunte a la API. Estoy usando esto para descargar archivos adjuntos, así que sé la identificación, el tipo de contenido y el nombre de archivo: estoy usando una API MVC para devolver el archivo:

[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)] public FileContentResult GetAttachment(Int32 attachmentID) { Attachment AT = filerep.GetAttachment(attachmentID); if (AT != null) { return new FileContentResult(AT.FileBytes, AT.ContentType); } else { return null; } }

La clase de archivo adjunto se ve así:

public class Attachment { public Int32 AttachmentID { get; set; } public string FileName { get; set; } public byte[] FileBytes { get; set; } public string ContentType { get; set; } }

El repositorio de filerep devuelve el archivo de la base de datos.

Espero que esto ayude a alguien :)


La descarga de archivos a través de ajax siempre es un proceso doloroso y, en mi opinión, es mejor dejar que el servidor y el navegador hagan este trabajo de negociación de tipo de contenido.

Creo que es mejor tener

<a href="api/sample/download"></a>

para hacerlo. Esto ni siquiera requiere que se abran ventanas nuevas y cosas así.

El controlador MVC como en su muestra puede ser como el siguiente:

[HttpGet("[action]")] public async Task<FileContentResult> DownloadFile() { // ... return File(dataStream.ToArray(), "text/plain", "myblob.txt"); }


Obtuve una solución para descargar desde angular 2 sin corromper, usando spring mvc y angular 2

Primero: mi tipo de retorno es: - ResponseEntity from java end. Aquí estoy enviando byte [] array tiene tipo de retorno desde el controlador.

2º- para incluir el protector de archivos en su espacio de trabajo, en la página de índice como:

<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2014-11-29/FileSaver.min.js"></script>

3º- en el componente ts escriba este código:

import {ResponseContentType} from ''@angular.core''; let headers = new Headers({ ''Content-Type'': ''application/json'', ''MyApp-Application'' : ''AppName'', ''Accept'': ''application/pdf'' }); let options = new RequestOptions({ headers: headers, responseType: ResponseContentType.Blob }); this.http .post(''/project/test/export'', somevalue,options) .subscribe(data => { var mediaType = ''application/vnd.ms-excel''; let blob: Blob = data.blob(); window[''saveAs''](blob, ''sample.xls''); });

Esto le dará el formato de archivo xls. Si desea otros formatos, cambie el tipo de medio y el nombre del archivo con la extensión correcta.


Para descargar y mostrar archivos PDF , un código recortado muy similar es el siguiente:

private downloadFile(data: Response): void { let blob = new Blob([data.blob()], { type: "application/pdf" }); let url = window.URL.createObjectURL(blob); window.open(url); } public showFile(fileEndpointPath: string): void { let reqOpt: RequestOptions = this.getAcmOptions(); // getAcmOptions is our helper method. Change this line according to request headers you need. reqOpt.responseType = ResponseContentType.Blob; this.http .get(fileEndpointPath, reqOpt) .subscribe( data => this.downloadFile(data), error => alert("Error downloading file!"), () => console.log("OK!") ); }


Para versiones angulares más nuevas:

npm install file-saver --save npm install @types/file-saver --save import {saveAs} from ''file-saver/FileSaver''; this.http.get(''endpoint/'', {responseType: "blob", headers: {''Accept'': ''application/pdf''}}) .subscribe(blob => { saveAs(blob, ''download.pdf''); });


Prueba esto !

1 - Instalar dependencias para mostrar guardar / abrir archivo emergente

npm install file-saver --save npm install @types/file-saver --save

2- Crea un servicio con esta función para recibir los datos

downloadFile(id): Observable<Blob> { let options = new RequestOptions({responseType: ResponseContentType.Blob }); return this.http.get(this._baseUrl + ''/'' + id, options) .map(res => res.blob()) .catch(this.handleError) }

3- En el componente, analiza el blob con ''file-saver''

import {saveAs as importedSaveAs} from "file-saver"; this.myService.downloadFile(this.id).subscribe(blob => { importedSaveAs(blob, this.fileName); } )

Esto funciona para mi!


Será mejor si intenta llamar al nuevo método dentro de su subscribe

this._reportService.getReport() .subscribe((data: any) => { this.downloadFile(data); }, (error: any) => сonsole.log(error), () => console.log(''Complete'') );

Dentro de la función downloadFile(data) necesitamos crear block, link, href and file name

downloadFile(data: any, type: number, name: string) { const blob = new Blob([data], {type: ''text/csv''}); const dataURL = window.URL.createObjectURL(blob); // IE doesn''t allow using a blob object directly as link href // instead it is necessary to use msSaveOrOpenBlob if (window.navigator && window.navigator.msSaveOrOpenBlob) { window.navigator.msSaveOrOpenBlob(blob); return; } const link = document.createElement(''a''); link.href = dataURL; link.download = ''export file.csv''; link.click(); setTimeout(() => { // For Firefox it is necessary to delay revoking the ObjectURL window.URL.revokeObjectURL(dataURL); }, 100); } }


Si no necesita agregar encabezados en la solicitud, para descargar un archivo en Angular2 puede hacer lo siguiente:

window.location.href=''http://example.com/myuri/report?param=x'';

en su componente


Si solo envía los parámetros a una URL, puede hacerlo de esta manera:

downloadfile(runname: string, type: string): string { return window.location.href = `${this.files_api + this.title +"/"+ runname + "/?file="+ type}`; }

en el servicio que recibe los parámetros


Simplemente ponga la url como href como se muestra a continuación.

<a href="my_url">Download File</a>


También puede descargar un archivo directamente desde su plantilla donde utiliza el atributo de descarga y en [attr.href] puede proporcionar un valor de propiedad del componente. Esta solución simple debería funcionar en la mayoría de los navegadores.

<a download [attr.href]="yourDownloadLink"></a>

Referencia: w3schools.com/tags/att_a_download.asp


<a href="my_url" download="myfilename">Download file</a>

my_url debe tener el mismo origen, de lo contrario, se redirigirá a esa ubicación


Para aquellos que usan Redux Pattern

Agregué en el protector de archivos como @Hector Cuevas nombrado en su respuesta. Usando Angular2 v. 2.3.1, no necesitaba agregar el @ types / file-saver.

El siguiente ejemplo es descargar una revista como PDF.

Las acciones del diario

public static DOWNLOAD_JOURNALS = ''[Journals] Download as PDF''; public downloadJournals(referenceId: string): Action { return { type: JournalActions.DOWNLOAD_JOURNALS, payload: { referenceId: referenceId } }; } public static DOWNLOAD_JOURNALS_SUCCESS = ''[Journals] Download as PDF Success''; public downloadJournalsSuccess(blob: Blob): Action { return { type: JournalActions.DOWNLOAD_JOURNALS_SUCCESS, payload: { blob: blob } }; }

Los efectos del diario

@Effect() download$ = this.actions$ .ofType(JournalActions.DOWNLOAD_JOURNALS) .switchMap(({payload}) => this._journalApiService.downloadJournal(payload.referenceId) .map((blob) => this._actions.downloadJournalsSuccess(blob)) .catch((err) => handleError(err, this._actions.downloadJournalsFail(err))) ); @Effect() downloadJournalSuccess$ = this.actions$ .ofType(JournalActions.DOWNLOAD_JOURNALS_SUCCESS) .map(({payload}) => saveBlobAs(payload.blob, ''journal.pdf''))

El servicio de revista

public downloadJournal(referenceId: string): Observable<any> { const url = `${this._config.momentumApi}/api/journals/${referenceId}/download`; return this._http.getBlob(url); }

El servicio HTTP

public getBlob = (url: string): Observable<any> => { return this.request({ method: RequestMethod.Get, url: url, responseType: ResponseContentType.Blob }); };

El reductor de diario Aunque esto solo establece los estados correctos utilizados en nuestra aplicación, todavía quería agregarlo para mostrar el patrón completo.

case JournalActions.DOWNLOAD_JOURNALS: { return Object.assign({}, state, <IJournalState>{ downloading: true, hasValidationErrors: false, errors: [] }); } case JournalActions.DOWNLOAD_JOURNALS_SUCCESS: { return Object.assign({}, state, <IJournalState>{ downloading: false, hasValidationErrors: false, errors: [] }); }

Espero que esto sea útil.


let headers = new Headers({ ''Content-Type'': ''application/json'', ''MyApp-Application'': ''AppName'', ''Accept'': ''application/vnd.ms-excel'' }); let options = new RequestOptions({ headers: headers, responseType: ResponseContentType.Blob }); this.http.post(this.urlName + ''/services/exportNewUpc'', localStorageValue, options) .subscribe(data => { if (navigator.appVersion.toString().indexOf(''.NET'') > 0) window.navigator.msSaveBlob(data.blob(), "Export_NewUPC-Items_" + this.selectedcategory + "_" + this.retailname +"_Report_"+this.myDate+".xlsx"); else { var a = document.createElement("a"); a.href = URL.createObjectURL(data.blob()); a.download = "Export_NewUPC-Items_" + this.selectedcategory + "_" + this.retailname +"_Report_"+this.myDate+ ".xlsx"; a.click(); } this.ui_loader = false; this.selectedexport = 0; }, error => { console.log(error.json()); this.ui_loader = false; document.getElementById("exceptionerror").click(); });