你还不会根据Url下载文件?

2,380 阅读3分钟

大家好,我是喜欢折腾,热爱分享的“一只韩非子”。
关注微信公众号:会编程的韩非子
添加微信号:Hanfz0712
免费加入问答群/知识交流群,一起交流技术难题与未来,让我们Geek起来!

最近遇到了一个需求,是需要前端通过Url去下载文件,于是研究了一波,便想着跟大家分享一下。 废话不多说,我们直接开始正文。

1.链接强制下载

最简单方式就是需要让服务端,给这个下载地址设置response header,使得url具有强制下载属性。 具体的设置:

Content-Disposition: attachment; filename="image.jpg"

这个header的意思是设置文件显示方式为直接下载,默认的文件名为image.jpg。

这里简单介绍下Content-Disposition:
Content-Disposition是 MIME 协议的扩展,MIME 协议指示 MIME 用户代理如何显示附加的文件。它有两个值:inline (默认值)将文件内容直接显示在页面、attachment 弹出对话框让用户下载。

2.a标签下载

a标签下载是最简单的方式,只需要给a标签附加download属性即可。

<a href="https://example.jpg" download="demo.jpg"></a>

这个a标签的意思是点击下载href中地址的文件,并默认文件名为demo.jpg。如果你不需要自定义名字也可以只写download属性,不要带后面的内容。
一切都是那么的美好,但是简单的代价就是有缺陷,这种方式会存在跨域问题

3.a标签下载(但完全体)

为了解决跨域问题,我们只能另辟蹊径,不能只用一个a标签了。没办法,只能使大招了!

// 思路:使用fetch获取到二进制文件,再将二进制文件转成Url. 然后通过a标签的href设置地址,download设置下载
// 为什么不直接给a标签设置地址?答:解决跨域问题
const download = async (url) => {
    // 获取文件名
    const fileName = url.substring(url.lastIndexOf('/') + 1)
    // 根据url获取文件内容
    const response = await fetch(url);
    if (!response.ok) {
        throw new Error('请求下载文件地址Error');
    }
    // 将文件内容转成二进制
    const blob = await response.blob();
    // 将二进制转成url
    const urlBlob = window.URL.createObjectURL(blob);

    const a = document.createElement('a');
    a.href = urlBlob;
    // 你可以设置一个默认的文件名
    a.download = filename;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);

    // 释放内存
    window.URL.revokeObjectURL(urlBlob);
}

细心的小伙伴发现了,这啥呀,这不还是用了a标签下载吗?确实确实,我们最终的方式还是用了a标签下载,我们先获取文件内容然后转成二进制,又把二进制转成了url。绕了一圈我们又回来了,但是这么做的目的就是为了解决跨域问题!

你发现这和我们人生一样,兜兜转转几十年我们又回到了起点,好似回到了我们刚出生的时候,一无所有,但又拥有者最美好的东西。

总结

设置Content-Disposition局限性太大了,不太推荐。
如果不存在跨域问题就直接a标签下载。
如果存在跨域,就上终极解决方案,绕圈圈!

2024/09/30 注意:await fetch(url)也有跨域问题,需要给oss设置允许跨域