how to get Content-Disposition in angular 17

Fork Me On Github

Introduction

Content-Disposition is the response header in the http request,Usually used to declare the filename of the response content.

In Angular apps, The content obtained through the API request needs to be implemented in code in order to be correctly output as downloadable content.So you need to get a filename.

Goals

In angular, by default, you can only manually add the file type and filename in the front-end code.

ts
                                        
export class DownloadService {
    constructor(private http: HttpClient) { }

    /**
     * Blob Request
     */
    public requestBlob(url: string, data?: any): Observable<any> {
        return this.http.request('post', url, {
            body: data,
            observe: 'response',
            responseType: 'blob',
        });
    }

    /**
     * Blob file conversion download
     */
    public downFile(result: any, fileName: string, fileType?: string) {
        const data = result.body;
        const blob = new Blob([data], {
                type: fileType || data.type,
            });
        const objectUrl = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.setAttribute('style', 'display:none');
        a.setAttribute('href', objectUrl);
        a.setAttribute('download', fileName);
        a.click();
        URL.revokeObjectURL(objectUrl);
    }

    public export(url: string, data: any, fileName: string, fileType?: any) {
        this.requestBlob(url, data).subscribe(result => {
            const headers = result.headers as HttpHeaders;
            this.downFile(result, fileName,
                fileType || headers.get('Content-Type'));
        });
    }

}
12345678910111213141516171819202122232425262728293031323334353637383940

Example of a call

ts
    
private downloadService: DownloadService

this.downloadService.export('http://localhost/export', {}, 'log.xlsx');
1234

Suddenly it occurred to me

The file type and filename are already in the response header, so is it possible to get them directly?

Getting started

Generally angular apps are single page apps with separate front and back ends.

And such applications are usually not under the same domain name.

Therefore, the CORS cross-domain resource sharing mechanism needs to be used.

The way to do this is to require the server to add some special response headers to the response header.

The Access-Control-Expose-Headers response header allows a server to indicate which response headers should be made available to scripts running in the browser, in response to a cross-origin request.

This way angular application can directly get the file name.

Relevant knowledge

Response Headers Access-Control-Expose-Headers lists which headers can be exposed externally as part of a response.

By default, only seven simple response headers can be exposed externally:

  1. Cache-Control
  2. Content-Language
  3. Content-Length
  4. Content-Type
  5. Expires
  6. Last-Modified
  7. Pragma

If you want to make other header information accessible to the client, you can list them inside Access-Control-Expose-Headers.

 
Access-Control-Expose-Headers: <header-name>, <header-name>, ...
1

Separate more than one by English commas

Final code

ts
                                                   
export class DownloadService {
    constructor(private http: HttpClient) { }

    /**
     * Blob Request
     */
    public requestBlob(url: string, data?: any): Observable<HttpResponse<Blob>> {
        return this.http.request('post', url, {
            body: data,
            observe: 'response',
            responseType: 'blob',
        });
    }

    /**
     * Blob file conversion download
     */
    public downFile(result: HttpResponse<Blob>, fileName?: string, fileType?: string) {
        fileName = this.parseFileName(result.headers.get('Content-Disposition'), fileName);
        if (!fileName) {
            console.log('fileName error');
            return;
        }
        const data = result.body;
        const blob = new Blob([data], {
                type: fileType || data.type,
            });
        const objectUrl = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.setAttribute('style', 'display:none');
        a.setAttribute('href', objectUrl);
        a.setAttribute('download', fileName);
        a.click();
        URL.revokeObjectURL(objectUrl);
    }

    public export(url: string, data: any, fileName?: string, fileType?: any) {
        this.requestBlob(url, data).subscribe((res: HttpResponse<Blob>) => {
            this.downFile(res, fileName, fileType);
        });
    }

    private parseFileName(header: string, def?: string): string {
        if (!header) {
            return def;
        }
        const name = header.split(';')[1].trim().split('=')[1];
        return decodeURI(name.replace(/"/g, ''));  // Note that non-English content please add url encoding on the server side
    }

}
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051
Click here to view
0 67 0