Angular (5) httpclient observe and responseType: 'blob'
Asked Answered
N

3

53

Context: I'm trying to download a binary file from a backend (that requires some data posted as json-body) and save it with file-saver using the filename specified by the backend in the content-disposition header. To access the headers I think I need the HttpResponse.

But I'm unable to use angular's HttpClient.post<T>(...): Observable<HttpResponse<T>>; method with a Blob.

When I call

this.httpclient.post<Blob>('MyBackendUrl', 
        params, 
        {observe: 'response', responseType: 'blob'});
the compiler complains about the 'blob' ('json' is accepted by the compiler):


error TS2345: Argument of type '{ observe: "response"; responseType: "blob"; }' is not assignable to parameter of type '{ headers?: HttpHeaders | { [header: string]: string | string[]; }; observe?: "body"; params?: Ht...'.
  Types of property 'observe' are incompatible.
    Type '"response"' is not assignable to type '"body"'.

When I put the options in an own object as seen in https://mcmap.net/q/340402/-using-the-request-method-in-angular-39-s-httpclient-class (but without the "as" ...) the post(...):Observable is called and I cannot access the headers.

Btw, even the simple example return this.http.get<Blob>('backendUrl', {responseType: 'blob'}); as seen e.g. in https://mcmap.net/q/340403/-angular-4-3-getting-an-arraybuffer-with-new-httpclient doesn't work for me.

Versions used

  • Angular Version: 5.0.3 (will be updated to latest 5 in a week or so)
  • typescript: 2.4.2
  • webpack: 3.8.1
Nert answered 16/3, 2018 at 11:6 Comment(1)
I'm sorry, I can't seem to get your issue : you can't read headers from the response, or you want to download a blob file ?Metrics
O
97

When using observe:response, don't type the call (post<Blob>(...)), as the returned Observable will be of HttpResponse. So this should work:

this.httpclient.post('MyBackendUrl', 
    params,
    {observe: 'response', responseType: 'blob'}
);

Why this happens, is there's two versions of the post method, one with a generic type, one without:

/**
     * Construct a POST request which interprets the body as JSON and returns the full event stream.
     *
     * @return an `Observable` of all `HttpEvent`s for the request, with a body type of `T`.
     */
    post<T>(url: string, body: any | null, options: {
        headers?: HttpHeaders | {
            [header: string]: string | string[];
        };
        observe: 'events';
        params?: HttpParams | {
            [param: string]: string | string[];
        };
        reportProgress?: boolean;
        responseType?: 'json';
        withCredentials?: boolean;
    }): Observable<HttpEvent<T>>;
    /**
     * Construct a POST request which interprets the body as an `ArrayBuffer` and returns the full response.
     *
     * @return an `Observable` of the `HttpResponse` for the request, with a body type of `ArrayBuffer`.
     */
    post(url: string, body: any | null, options: {
        headers?: HttpHeaders | {
            [header: string]: string | string[];
        };
        observe: 'response';
        params?: HttpParams | {
            [param: string]: string | string[];
        };
        reportProgress?: boolean;
        responseType: 'arraybuffer';
        withCredentials?: boolean;
    }): Observable<HttpResponse<ArrayBuffer>>;
Odel answered 16/3, 2018 at 12:48 Comment(3)
Thanks. I hadn't realized that adding the responseType: 'blob' implicitly changes the result to Observable<Blob> / Obserrvable<HttpResponse<Blob>>Nert
Awesome answer...!Kwei
Thank you @KumaresanSd I hope it's still relevant with NG 13 after 4 years :D I'd guess it is :)Odel
T
29

you can use as like

responseType: 'blob' as 'json'
Timoteo answered 30/9, 2019 at 12:39 Comment(2)
this would select a different version of the method in httpClient, would it not?Plaice
from angualr 4 (http client introduce), you can use it, from this versionTimoteo
C
24

Other answers are right but they are missing the example.

The main answer first when the responseType is set the return type of the response is changed to Blob. To solve this add observe: 'response' which returns HTTPResponse.

Example: I stumbled upon this issue and spent 6 hours solving.

So, here I present an example to get filename from headers and download the file:

downloadPDF(url: string): Observable<any> {
return this.http.get<any>(url, { responseType: 'blob', observe: 'response' }).pipe(
  map((result:HttpResponse<Blob>) => {
    console.log(result);
    saveAs(result, "Quotation.pdf");
    return result;
  }));

Here the http is instance of HttpClient, saveAs() is a method of FileSaver npm package same as the OP.

There is one more problem you might get only 5 headers(Cache-Control, Pragma, etc) in the result.headers and not your custom header for e.g. x-filename.

The reason behind this CORS. Basically CORS doesn't allow browsers to access more than handfull of headers (listed in the link above).

So solve this you would have to change server/API to send Access-Control-Expose-Headers header with the request.

Conveyance answered 10/1, 2020 at 8:58 Comment(2)
Hi, i spent 1 hour, Thanks to you it was no more. Regards.Ester
I don't usually leave kudos comments, but this saved me a lot of time! Thank you!Nickles

© 2022 - 2024 — McMap. All rights reserved.