Angular: How to download a file from HttpClient?

AngularTypescriptFileBlobAngular Httpclient

Angular Problem Overview


I need download an excel from my backend, its returned a file.

When I do the request I get the error:

> TypeError: You provided 'undefined' where a stream was expected. You > can provide an Observable, Promise, Array, or Iterable.

My code is:

this.http.get(`${environment.apiUrl}/...`)
      .subscribe(response => this.downloadFile(response, "application/ms-excel"));

I tried get and map(...) but didn't work.

Details: angular 5.2

references:

import { HttpClient } from '@angular/common/http';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/finally';
import 'rxjs/add/operator/map'
import 'rxjs/add/operator/catch';

Content-Type of response:

Content-Type: application/ms-excel

What's wrong?

Angular Solutions


Solution 1 - Angular

Blobs are returned with file type from backend. The following function will accept any file type and popup download window:

downloadFile(route: string, filename: string = null): void{
   
    const baseUrl = 'http://myserver/index.php/api';
    const token = 'my JWT';
    const headers = new HttpHeaders().set('authorization','Bearer '+token);
    this.http.get(baseUrl + route,{headers, responseType: 'blob' as 'json'}).subscribe(
        (response: any) =>{
            let dataType = response.type;
            let binaryData = [];
            binaryData.push(response);
            let downloadLink = document.createElement('a');
            downloadLink.href = window.URL.createObjectURL(new Blob(binaryData, {type: dataType}));
            if (filename)
                downloadLink.setAttribute('download', filename);
            document.body.appendChild(downloadLink);
            downloadLink.click();
        }
    )
}

Solution 2 - Angular

Try something like this:

> type: application/ms-excel

/**
 *  used to get file from server
 */

this.http.get(`${environment.apiUrl}`,{
          responseType: 'arraybuffer',headers:headers} 
         ).subscribe(response => this.downLoadFile(response, "application/ms-excel"));


    /**
	 * Method is use to download file.
	 * @param data - Array Buffer data
	 * @param type - type of the document.
	 */
	downLoadFile(data: any, type: string) {
		let blob = new Blob([data], { type: type});
		let url = window.URL.createObjectURL(blob);
		let pwa = window.open(url);
        if (!pwa || pwa.closed || typeof pwa.closed == 'undefined') {
            alert( 'Please disable your Pop-up blocker and try again.');
        }
	}

Solution 3 - Angular

It took me a while to implement the other responses, as I'm using Angular 8 (tested up to 13). I ended up with the following code (heavily inspired by Hasan).

Note that for the name to be set, the header Access-Control-Expose-Headers MUST include Content-Disposition. To set this in django RF:

http_response = HttpResponse(package, content_type='application/javascript')
http_response['Content-Disposition'] = 'attachment; filename="{}"'.format(filename)
http_response['Access-Control-Expose-Headers'] = "Content-Disposition"

In angular:

  // component.ts
  // getFileName not necessary, you can just set this as a string if you wish
  getFileName(response: HttpResponse<Blob>) {
    let filename: string;
    try {
      const contentDisposition: string = response.headers.get('content-disposition');
      const r = /(?:filename=")(.+)(?:;")/
      filename = r.exec(contentDisposition)[1];
    }
    catch (e) {
      filename = 'myfile.txt'
    }
    return filename
  }

  
  downloadFile() {
    this._fileService.downloadFile(this.file.uuid)
      .subscribe(
        (response: HttpResponse<Blob>) => {
          let filename: string = this.getFileName(response)
          let binaryData = [];
          binaryData.push(response.body);
          let downloadLink = document.createElement('a');
          downloadLink.href = window.URL.createObjectURL(new Blob(binaryData, { type: 'blob' }));
          downloadLink.setAttribute('download', filename);
          document.body.appendChild(downloadLink);
          downloadLink.click();
        }
      )
  }

  // service.ts
  downloadFile(uuid: string) {
    return this._http.get<Blob>(`${environment.apiUrl}/api/v1/file/${uuid}/package/`, { observe: 'response', responseType: 'blob' as 'json' })
  }

Solution 4 - Angular

Using Blob out put from API (Excel FIle)

and tweaked @gabrielrincon answer

downloadExcel(): void {
const payload = {
  order: 'test',
  };

this.service.downloadExcel(payload)
  .subscribe((res: any) => {
    this.blobToFile(res, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "Export.xlsx");
  });}

blob to file common function

blobToFile(data: any, type: string, fileName: string) {
 const a = document.createElement('a');
 document.body.appendChild(a);
 a.style.display = 'none';
 const blob = new Blob([data], { type: type });
 const url = window.URL.createObjectURL(blob);
 a.href = url; a.download = fileName; a.click();
 window.URL.revokeObjectURL(url);}

in the blob to file function we are expecting first parameter as our blob data, type of file and pass file name including extention 1. we are creating an html a tag element 2. then we append the element in html 3. then hide the a tag element 4. then create new blob object with file and type 5. we will convert the blob object to URL 6. then appened that URL to href property of our a tag 7. we are opening our URL in window so it will download

Solution 5 - Angular

I ended up here when searching for ”rxjs download file using post”.

This was my final product. It uses the file name and type given in the server response.

import { ajax, AjaxResponse } from 'rxjs/ajax';
import { map } from 'rxjs/operators';

downloadPost(url: string, data: any) {
    return ajax({
        url: url,
        method: 'POST',
        responseType: 'blob',
        body: data,
        headers: {
            'Content-Type': 'application/json',
            'Accept': 'text/plain, */*',
            'Cache-Control': 'no-cache',
        }
    }).pipe(
        map(handleDownloadSuccess),
    );
}


handleDownloadSuccess(response: AjaxResponse) {
    const downloadLink = document.createElement('a');
    downloadLink.href = window.URL.createObjectURL(response.response);

    const disposition = response.xhr.getResponseHeader('Content-Disposition');
    if (disposition) {
        const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/;
        const matches = filenameRegex.exec(disposition);
        if (matches != null && matches[1]) {
            const filename = matches[1].replace(/['"]/g, '');
            downloadLink.setAttribute('download', filename);
        }
    }

    document.body.appendChild(downloadLink);
    downloadLink.click();
    document.body.removeChild(downloadLink);
}

Solution 6 - Angular

After spending much time searching for a response to this answer: how to download a simple image from my API restful server written in Node.js into an Angular component app, I finally found a beautiful answer in this web Angular HttpClient Blob. Essentially it consist on:

API Node.js restful:

   /* After routing the path you want ..*/
  public getImage( req: Request, res: Response) {

    // Check if file exist...
    if (!req.params.file) {
      return res.status(httpStatus.badRequest).json({
        ok: false,
        msg: 'File param not found.'
      })
    }
    const absfile = path.join(STORE_ROOT_DIR,IMAGES_DIR, req.params.file);

    if (!fs.existsSync(absfile)) {
      return res.status(httpStatus.badRequest).json({
        ok: false,
        msg: 'File name not found on server.'
      })
    }
    res.sendFile(path.resolve(absfile));
  }

Angular 6 tested component service (EmployeeService on my case):

  downloadPhoto( name: string) : Observable<Blob> {
    const url = environment.api_url + '/storer/employee/image/' + name;

    return this.http.get(url, { responseType: 'blob' })
      .pipe(
        takeWhile( () => this.alive),
        filter ( image => !!image));
  }

Template

 <img [src]="" class="custom-photo" #photo>

Component subscriber and use:

@ViewChild('photo') image: ElementRef;

public LoadPhoto( name: string) {
    this._employeeService.downloadPhoto(name)
          .subscribe( image => {
            const url= window.URL.createObjectURL(image);
            this.image.nativeElement.src= url;
          }, error => {
            console.log('error downloading: ', error);
          })    
}

Solution 7 - Angular

May be I am late. But last answer by @Hasan was superb.

I just make little changes(this was not accepting headers so removed) in this and got success.

> downloadFile(route: string, filename: string = null): void { > // const baseUrl = 'http://myserver/index.php/api';;
> this.http.get(route, { responseType: 'blob' }).subscribe( > (response: any) => { > let dataType = response.type; > let binaryData = []; > binaryData.push(response); > let downloadLink = document.createElement('a'); > downloadLink.href = window.URL.createObjectURL(new Blob(binaryData, { type: dataType })); > if (filename) { > downloadLink.setAttribute('download', filename); > } > document.body.appendChild(downloadLink); > downloadLink.click(); > } > ) > }

Solution 8 - Angular

Using Blob as a source for an img:

template:

<img [src]="url">

component:

 public url : SafeResourceUrl;

 constructor(private http: HttpClient, private sanitizer: DomSanitizer) {
   this.getImage('/api/image.jpg').subscribe(x => this.url = x)
 }

 public getImage(url: string): Observable<SafeResourceUrl> {
   return this.http
     .get(url, { responseType: 'blob' })
     .pipe(
       map(x => {
         const urlToBlob = window.URL.createObjectURL(x) // get a URL for the blob
         return this.sanitizer.bypassSecurityTrustResourceUrl(urlToBlob); // tell Anuglar to trust this value
       }),
     );
 }

Further reference about trusting save values

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJean CarlosView Question on Stackoverflow
Solution 1 - AngularHasanView Answer on Stackoverflow
Solution 2 - AngularAkjView Answer on Stackoverflow
Solution 3 - AngularPrestonView Answer on Stackoverflow
Solution 4 - AngularShabeer MView Answer on Stackoverflow
Solution 5 - AngularMikael MView Answer on Stackoverflow
Solution 6 - AngularMarcView Answer on Stackoverflow
Solution 7 - AngularDeepak DholiyanView Answer on Stackoverflow
Solution 8 - AngularRafi HenigView Answer on Stackoverflow