typescript - Angular (5) httpclient observe och responseType: 'blob'


Kontext: Jag försöker ladda ner en binär fil från ett backend (som kräver att viss data skickas som json-body) och spara den med file-saver med det filnamn som specificeras av backend i content-disposition-headern. För att få åtkomst till headern tror jag att jag behöver HttpResponse.

Men jag kan inte använda Angulars HttpClient.post<T>(...): Observable<HttpResponse<T>>;-metod med en Blob.

När jag ringer

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"'.

När jag lägger alternativen i ett eget objekt som ses i https://stackoverflow.com/a/48016652/2131459 (men utan "as" ...) anropas post(...):Observable och jag kan inte komma åt rubrikerna.

Förresten, även det enkla exemplet return this.http.get<Blob>('backendUrl', {responseType: 'blob'}); som ses t.ex. i https://stackoverflow.com/a/46882407/2131459 fungerar inte för mig.

Använda versioner

  • Angular-version: 5.0.3 (kommer att uppdateras till senaste 5 om en vecka eller så)
  • typescript: 2.4.2
  • webpack: 3.8.1

Svar #1

När du använder observe:response, skriv inte anropet (post<Blob>(...)), eftersom den returnerade Observable kommer att vara av HttpResponse-typ. Så detta bör fungera:

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

Varför detta händer är att det finns två versioner av postmetoden, en med en generisk typ, en utan:

/**
     * 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>>;

Svar #2

du kan använda som

responseType: 'blob' as 'json'

Svar #3

Andra svar är rätt men de saknar exemplet.

Huvudsvar först när responseType är inställt ändras returtypen för svaret till Blob. För att lösa detta, lägg till observe: 'response' vilket returnerar HTTPResponse.

Exempel: Jag snubblade över detta problem och tillbringade 6 timmar med att lösa det.

Så, här presenterar jag ett exempel för att få filnamnet från headers och ladda ner filen:

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;
  }));

Här är http en instans av HttpClient, saveAs() är en metod från FileSaver npm-paketet precis som hos OP.

Det finns ett problem till, du kan få endast 5 headers (Cache-Control, Pragma, etc) i result.headers och inte din anpassade header t.ex. x-filename.

Anledningen bakom detta CORS. I grunden tillåter inte CORS att webbläsare får åtkomst till mer än ett fåtal headers (listade i länken ovan).

Så för att lösa detta skulle du behöva ändra server/API för att skicka Access-Control-Expose-Headers-huvudet med förfrågan.

Relaterade artiklar