How to upload a file using Angular2

I have a WebApi / MVC application for which I am developing an angular2 client (instead of MVC). I am having trouble understanding how Angular saves a file.

The request is fine (works fine with MVC, and we can log the received data), but I can’t figure out how to save the downloaded data (I basically follow the same logic as in this post ). I'm sure it is stupidly simple, but so far I just do not understand it.

The component function code is shown below. I tried different alternatives, the blob path should be as I understood, but the createObjectURL function is not in the URL . I can’t even find the URL definition in the window, but apparently it exists. If I use the FileSaver.js module I get the same error. So, I think this is something that has changed recently or has not yet been implemented. How can I activate file saving in 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); } 

For completeness, the service that retrieves the data is below, but the only thing it does is send a request and pass the data without matching, if that succeeds:

 downloadfile(runname: string, type: string){ return this.authHttp.get( this.files_api + this.title +"/"+ runname + "/?file="+ type) .catch(this.logAndPassOn); } 
+145
angular download typescript fileapi
Feb 01 '16 at 19:15
source share
24 answers

The problem is that observable starts in a different context, so when you try to create the var URL, you have an empty object and not the BLOB object you need.

One of the many ways to solve this problem is as follows:

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

When the request is ready, it will call the "downloadFile" function, which is defined as follows:

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

The blob was created perfectly, so the var URL, if a new window does not open, check that you have already imported 'rxjs / Rx';

  import 'rxjs/Rx' ; 

I hope this helps you.

+144
Feb 04 '16 at 19:54
source share

Try it out !

1 - Set dependencies for the Save / Open popup

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

2- Create a service using this function to get data

 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- In the component, analyze the blob using a “file saver”,

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

It works for me!

+79
May 11 '17 at 10:24
source share

If you don’t need to add headers to the request in order to upload the file in Angular2, you can do simple:

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

in your component.

+44
Aug 04 '16 at 5:37
source share

This is for people who are watching how to do this using the HttpClient and file saver:

  1. Set file saver

npm install the --save splash file

npm install @ types / file-saver --save

API Service Class:

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

Component:

 import { saveAs } from 'file-saver'; exportPdf() { this.api_service.export().subscribe(data => saveAs(data, 'pdf report.pdf')); } 
+37
Jan 29 '18 at 14:13
source share

How about this?

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

I could handle it.
no need for an additional package.

+23
Jul 13 '17 at 0:28
source share

As Alejandro Corredor mentioned , this is a simple scale error. subscribe asynchronous, and open must be placed in this context for the data to finish loading when the download starts.

However, there are two ways to do this. Since the documents recommend that the service take care of the receipt and display of data:

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

Then, on the component, we simply subscribe and process the displayed data. There are two possibilities. The first , as suggested in the original publication, but needs a little correction, as Alejandro noted:

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

The second way is to use FileReader. The logic is the same, but we can clearly expect FileReader to load the data, avoiding nesting and solving the async problem.

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

Note. I am trying to download an Excel file, and even though the download has been initiated (so that answers the question), the file is damaged. See the answer to this post to avoid a damaged file.

+19
Feb 05 '16 at 15:35
source share

Download the * .zip solution for angular 2.4.x: you have to import ResponseContentType from '@ angular / http' and change responseType to ResponseContentType.ArrayBuffer (by default, this is 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']); } 
+16
Feb 12 '17 at 14:58
source share

For newer corner versions:

 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'); }); 
+12
Jun 29 '18 at 15:33
source share

Uploading a file through ajax is always a painful process, and, in my opinion, it is better to allow the server and browser to perform this work on the coordination of content types.

I believe the best

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

to do this. It does not even require opening new windows and the like.

The MVC controller, as in your example, might look like this:

 [HttpGet("[action]")] public async Task<FileContentResult> DownloadFile() { // ... return File(dataStream.ToArray(), "text/plain", "myblob.txt"); } 
+10
Jan 20 '17 at 5:13
source share

I share the solution that helped me (any improvement is greatly appreciated)

On your pservice service :

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

Component part:

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

On the component part, you call the service without signing up for a response. Subscribe to the full list of openOffice mime types: http://www.openoffice.org/framework/documentation/mimetypes/mimetypes.html

+6
Jan 18 '17 at 14:16
source share

For those who use the Redux template

I added to the file-keeper as @Hector Cuevas, named in his answer. Using Angular2 v. 2.3.1, I did not need to add to @ types / file-saver.

The next example is downloading a magazine in PDF format.

Journal Actions

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

Magazine Effect

 @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')) 

Magazine Service

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

HTTP service

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

Log reducer Although this only sets the correct states used in our application, I still wanted to add it to show the complete template.

 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: [] }); } 

Hope this is helpful.

+6
Aug 17 '17 at 9:01 on
source share

It will be better if you try to call a new method inside your subscribe

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

Inside the downloadFile(data) function, we need to make 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); } } 
+6
Aug 20 '18 at 11:07
source share

To download and show PDF files, very similar code is disabled:

  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!") ); } 
+5
Jun 15 '17 at 11:40
source share

I am using Angular 4 with a 4.3 httpClient object. I modified the answer I found on the Js Tech Blog that creates a link object, uses it to download, and then destroys it.

Client:

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

The value of this.downloadUrl was set earlier to point to api. I use this to load attachments, so I know id, contentType and filename: I use MVC api to return the file:

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

The application class is as follows:

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

The filerep repository returns a file from the database.

Hope this helps someone :)

+5
May 01 '18 at 1:31
source share

Update Hector's answer using the file saver and HttpClient for step 2:

 public downloadFile(file: File): Observable<Blob> { return this.http.get(file.fullPath, {responseType: 'blob'}) } 
+4
Aug 23 '18 at 20:50
source share

Here is what I did in my case -

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

The solution is referenced - here

+4
01 Oct '18 at 10:07
source share

I got a solution to boot from corner 2 without damage using spring mvc and corner 2

1st- my return type: - ResponseEntity from the java end. Here I am sending a byte [] array with the return type from the controller.

2nd- enable the file manager in the workspace - on the index page:

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

3rd- in the ts component write this code:

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

This will give you the xls file format. If you want other formats to change the media type and file name with the correct extension.

+3
Jul 16 '17 at 12:33
source share

Today I faced the same case, I had to download the PDF file as an attachment (the file should not be displayed in the browser, but should be loaded instead). To achieve this, I found that I had to get the file in Angular Blob , and at the same time add the Content-Disposition header to the response.

This was the simplest thing I could get (Angular 7):

Inside the service:

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

Then, when I need to upload the file to the component, I can simply:

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

UPDATE:

Removed unnecessary header settings from the service

+3
May 10 '19 at 9:06
source share
  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(); }); 
+1
Aug 19 '18 at 17:20
source share

Just enter url as href as href below.

 <a href="my_url">Download File</a> 
+1
Dec 07 '18 at 11:00
source share

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

my_url must have the same origin, otherwise it will be redirected to this place

+1
May 7 '19 at 11:28
source share

You can also download the file directly from your template, where you use the upload attribute, and in [attr.href] you can specify the property value from the component. This simple solution should work in most browsers.

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

Link: https://www.w3schools.com/tags/att_a_download.asp

+1
Aug 14 '19 at 7:07
source share

If you only send parameters to a URL, you can do it like this:

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

in a service that receives parameters

0
Jul 13 '19 at 23:27
source share

This answer assumes that you cannot upload files directly using AJAX, primarily for security reasons. Therefore, I will describe what I do in this situation,

01. Add the href attribute to your anchor tag inside the component.html file,
eg: -

 <div> <a [href]="fileUrl" mat-raised-button (click)='getGenaratedLetterTemplate(element)'> GENARATE </a> </div> 

02. Complete all of the following steps in component.ts to bypass the security level and open the Save As pop-up dialog box,
eg: -

 import { environment } from 'environments/environment'; import { DomSanitizer } from '@angular/platform-browser'; export class ViewHrApprovalComponent implements OnInit { private apiUrl = environment.apiUrl; fileUrl constructor( private sanitizer: DomSanitizer, private letterService: LetterService) {} getGenaratedLetterTemplate(letter) { this.data.getGenaratedLetterTemplate(letter.letterId).subscribe( // cannot download files directly with AJAX, primarily for security reasons); console.log(this.apiUrl + 'getGeneratedLetter/' + letter.letterId); this.fileUrl = this.sanitizer.bypassSecurityTrustResourceUrl(this.apiUrl + 'getGeneratedLetter/' + letter.letterId); } 

Note. This answer will work if you get an "OK" error message with a status code of 200.

0
Sep 17 '19 at 6:25
source share



All Articles