Fork Me On Github

需求

angular 实现文件下载功能, 默认只能在前端代码中手动添加文件类型及文件名。

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

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

    /**
     * Blob文件转换下载
     */
    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'));
        });
    }

}

使用

private downloadService: DownloadService

this.downloadService.export('http://localhost/export', {}, '流水记录.xlsx');

突然想到

在响应头中已经有了文件类型和文件名,那么是否可以直接获取呢?

解决

关键是响应头中的 Access-Control-Expose-Headers

angular issue 中就有人提处理这个问题,

并且给出了解决方法

Unable to view 'Content-Disposition' headers in Angular4 GET response

里面提供了一个 Java 的解决方案

翻译成普通语言就是:

需要在服务端响应头中加 Access-Control-Expose-Headers 加上 Content-Disposition

Access-Control-Expose-Headers: Content-Disposition

基础知识

响应首部 Access-Control-Expose-Headers 列出了哪些首部可以作为响应的一部分暴露给外部。

默认情况下,只有七种 simple response headers (简单响应首部)可以暴露给外部:

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

如果想要让客户端可以访问到其他的首部信息,可以将它们在 Access-Control-Expose-Headers 里面列出来。

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

多个用英文逗号分隔

来源

最终代码

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

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

    /**
     * Blob文件转换下载
     */
    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, '')); // 注意中文请在服务端添加url编码
    }

}
点击查看全文
标签: angular
0 63 0
angular 12使用 KaTex 显示 AsciiMath 格式的公式
ng-deep 使用
angular 12 动态生成组件
angular 12 动画执行完成事件
angular 全局搜索触发子模块的搜索