8

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?

2
  • 2
    Have you also tried to use responseType: 'blob' + removing the new Blob()? Commented Oct 22, 2017 at 20:40
  • Thanks at lot! It's working! Commented Oct 23, 2017 at 5:09

2 Answers 2

12

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;
}
Sign up to request clarification or add additional context in comments.

Comments

5

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.

1 Comment

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.