Angular 4.3: Getting an arraybuffer with new HttpClient

24,972

Solution 1

So Martin has solved my issue:

getXlsx (): Observable<any> {
    return this.http.get('api/xlsx', {
      responseType: 'blob' // <-- changed to blob
    })
    .map(res => downloadFile(res, 'application/xlsx', 'export.xlsx'))
    .catch(err => handleError(err));
  }

export function downloadFile(blob: any, type: string, filename: string): string {
  const url = window.URL.createObjectURL(blob); // <-- work with blob directly

  // create hidden dom element (so it works in all browsers)
  const a = document.createElement('a');
  a.setAttribute('style', 'display:none;');
  document.body.appendChild(a);

  // create file, attach to hidden element and open hidden element
  a.href = url;
  a.download = filename;
  a.click();
  return url;
}

Solution 2

The above works and is an acceptable solution, however seems like a code smell just adding anchor tags to the DOM and faking a click when you can do it in a much cleaner way. We've recently had a similar issue for downloading documents in general from an Angular 5 website in which we have used FileSaver(https://www.npmjs.com/package/file-saver) .

Adding FileSaver using npm install file-saver and doing the relevant imports you can use the following code to download a file:

getDocument(document: Document) {
    let headers = new HttpHeaders(); // additional headers in here

    return this._http.get(url, {
        headers: headers,
        responseType: "blob" // this line being the important part from the previous answer (thanks for that BTW Martin) 
    }).map(
        res => {
            var x = res;
            if (res) {
                let filename = documentName;
                saveAs(x, filename);
            }
            return true;
        },
        err => {
            return true;
        }
    );
} 

This uses the native saveAs command if it exists and implements some other logic to replicate the functionality if it doesn't.

This may do a similar thing under the hood (i don't really know as haven't had the change to look), but it compartmentalises it in an easy to use third party package that I would hope would be maintained (fingers crossed) without me having to update functionality to cater for newer versions of different packages / browsers.

Share:
24,972
Luke
Author by

Luke

Updated on June 29, 2020

Comments

  • Luke
    Luke almost 4 years

    I would like to change to the new HttpClient. Until now I handle file downloads the following:

    getXlsx (): Observable<any> {
        return this.http.get('api/xlsx', {
          responseType: ResponseContentType.ArrayBuffer, // set as ArrayBuffer instead of Json
        })
        .map(res => downloadFile(res, 'application/xlsx', 'export.xlsx'))
        .catch(err => handleError(err));
      }
    
    export function downloadFile(data: any, type: string, filename: string): string {
      const blob = new Blob([data._body], { type });
      const url = window.URL.createObjectURL(blob);
    
      // create hidden dom element (so it works in all browsers)
      const a = document.createElement('a');
      a.setAttribute('style', 'display:none;');
      document.body.appendChild(a);
    
      // create file, attach to hidden element and open hidden element
      a.href = url;
      a.download = filename;
      a.click();
      return url;
    }
    

    Changing the respondeType to 'arraybuffer' will result in empty files. Any ideas how to solve it?

  • Drenai
    Drenai almost 3 years
    Basically file-saver seems to do the same thing, but it also adds a lot of cross browser checks and fallbacks that cover numerous edge cases👍 github.com/eligrey/FileSaver.js/blob/master/src/FileSaver.js