惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

月光博客
月光博客
人人都是产品经理
人人都是产品经理
博客园 - 聂微东
WordPress大学
WordPress大学
S
SegmentFault 最新的问题
博客园 - Franky
V
V2EX
Y
Y Combinator Blog
Google DeepMind News
Google DeepMind News
J
Java Code Geeks
T
The Blog of Author Tim Ferriss
罗磊的独立博客
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Jina AI
Jina AI
博客园 - 叶小钗
F
Fortinet All Blogs
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
A
About on SuperTechFans
M
MIT News - Artificial intelligence
云风的 BLOG
云风的 BLOG
Last Week in AI
Last Week in AI
D
Docker
博客园 - 【当耐特】
阮一峰的网络日志
阮一峰的网络日志

zodream梦想开源/个人编程日记

文件解析笔记-zodream梦想开源/个人编程日记 密码本开发笔记之读写与保存-zodream梦想开源/个人编程日记 SkiaSharp 把 pixel byte[] 转成 SKBitmap-zodream梦想开源/个人编程日记 nas 使用 Docker 安装 gogs-zodream梦想开源/个人编程日记 复制 android 手机中的文件到电脑-zodream梦想开源/个人编程日记 周报:寻找优质的周刊-zodream梦想开源/个人编程日记 开发日志:对Markdown的代码块新增引用来源支持-zodream梦想开源/个人编程日记 周报:怎么写技术类的教程文章-zodream梦想开源/个人编程日记 css display:flex 布局尺寸超出问题-zodream梦想开源/个人编程日记 周报:SEO优化的思考-zodream梦想开源/个人编程日记 Edge 浏览器不适用 Edge Image Viewer 打开图片 -zodream梦想开源/个人编程日记 SEO 学习笔记(一) 内容来源-zodream梦想开源/个人编程日记 PHP 实现双因素身份认证(2FA)-zodream梦想开源/个人编程日记 WPF MVVM 获取List 多选数据-zodream梦想开源/个人编程日记 Burp Suite 抓包-zodream梦想开源/个人编程日记 使用 indexnow 注意事项-zodream梦想开源/个人编程日记 Godot 使用字体图标 例如: Iconfont、FontAwesome-zodream梦想开源/个人编程日记 angular 15 对指定页面进行访问限制-zodream梦想开源/个人编程日记 CSS 使用 column-count 实现瀑布流出现内容分割的解决办法-zodream梦想开源/个人编程日记 input 确认按键事件在手机端不生效-zodream梦想开源/个人编程日记 C# 使用socket 进行通讯-zodream梦想开源/个人编程日记 Maui开发中Windows应用开启管理员权限-zodream梦想开源/个人编程日记 Maui 中自定义控件-zodream梦想开源/个人编程日记 angular 14 使用 ng-template 实现tree 结构显示-zodream梦想开源/个人编程日记 c# 动态安装和卸载dll-zodream梦想开源/个人编程日记 慎用 CompositionTarget.Rendering-zodream梦想开源/个人编程日记 c# 重写 c++ 程序笔记:数据初始化-zodream梦想开源/个人编程日记 源码编译 aseprite-zodream梦想开源/个人编程日记 记录一下字符串分隔split各语言之间的不同-zodream梦想开源/个人编程日记 c# Gzip解码无头内容-zodream梦想开源/个人编程日记
angular 11 怎么获取 Content-Disposition-zodream梦想开源/...
zodream · 2021-04-13 · via zodream梦想开源/个人编程日记

需求

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

}

12345678910111213141516171819202122232425262728293031323334353637383940

使用

private downloadService: DownloadService

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

1234

突然想到

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

解决

关键是响应头中的 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

1

基础知识

响应首部 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>, ...

1

多个用英文逗号分隔

来源

最终代码

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编码
    }

}

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051

转载请保留原文链接: https://zodream.cn/blog/id/201.html