Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular 4.3: Getting an arraybuffer with new HttpClient

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?

like image 350
Luke Avatar asked Oct 22 '17 19:10

Luke


People also ask

What is httpclient get in angular?

HTTP GET. The HttpClient.get sends the HTTP Get Request to the API endpoint and parses the returned result to the desired type. By default, the body of the response is parsed as JSON. If you want any other type, then you need to specify explicitly using the observe & responseType options. You can read more about Angular HTTP Get. Syntax

How do I send an HTTP request in angular?

Using Angular HttpClient. The HttpClient is a separate model in Angular and is available under the @angular/common/http package. All you need to do is to import it and inject it into our component/service. Then, Use HttpClient.Get method to send an HTTP Request and Subscribe to the response Asynchronously.

How to create a service using angular CLI?

Creating services with the help of angular cli is one of the best ways to do so. You can create one manually but its always easier to generate it using the CLI as it uses the latest syntax and creates the service, its spec and adds it to the nearest module as a provider.

How to run angular application on localhost?

Run the Angular application with help of angular-cli command ng serve, and you will get a message like, “Angular Live Development Server is listening on localhost:4200. Open your browser on http://localhost:4200/.” Once you open the URL in your browser, you will get an output like the image below.


2 Answers

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;
}
like image 118
Luke Avatar answered Oct 21 '22 10:10

Luke


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.

like image 37
chunkydigits Avatar answered Oct 21 '22 10:10

chunkydigits