文章目录
一、什么是同源和不同源文件
1.1. 文件的地址两种情况
- 文件存在于自身项目中 如:
@/assets/img/xxx.png - 文件的地址是在线地址,如:
http://xxx.png或者https://xxx.png
1.2. 什么是同源文件
- 对于存在于自身项目中的文件,一定是同源文件
- 对于在线文件地址,则必须遵守同源策略(协议域名和端口号一致)才是同源文件
二、同源文件的下载
2.1. 方式一:直接使用a标签下载
对于同源文件的下载可以直接使用a标签,配合download属性即可下载,如果download给了属性值,那么属性值就是文件名。
<!-- a链接下载,同源download属性没有任何问题,默认都是下载文件; download="fileName" 不指定,默认就是当前资源文件名-->
<a href="./assets/note.txt" download="载txt">下载txt</a>
<a href="./assets/info.json" download="下载JSON">下载JSON</a>
<a href="./assets/avatar.jpg" download="下载png图片">下载png图片</a>
<a href="./assets/avatar.zip" download="下载zip压缩包">下载zip压缩包</a>
<a href="./assets/markdown.md" download="下载markdown文件">下载markdown文件</a>
<a href="./assets/JavaScript高级程序设计(第4版 中文高清).pdf" download="下载PDF">下载PDF</a>
<a href="./assets/销售统计表.xlsx" download>下载Excel</a>
<a href="./assets/毕业论文.docx" download>下载Word</a>
<a href="./assets/述职报告.pptx" download>下载PPT</a>
2.2. 方式二:封装下载方法(原理依旧是a标签)
const downloadByUrl = (url, filename) => {
if (!url) throw new Error('当前没有下载链接');
const a = document.createElement("a");
a.style.display = "none";
a.href = url;
a.download = filename;
// 使用target="_blank"时,添加rel="noopener noreferrer" 堵住钓鱼安全漏洞 防止新页面window指向之前的页面
a.rel = "noopener noreferrer";
document.body.append(a);
a.click();
setTimeout(() => {
a.remove();
}, 1000);
};
二、不同源文件下载
2.1. a标签的缺点
2.1.1. 同源 URL 的限制
download 只在同源 URL 、 blob: 、 data: 协议起作用
也就是说跨域是下载不了的…(这种说法不全对,除非后端配置 Content-Disposition 为 attachment,后面会讲)
a标签在下载第三方资源(非同源)文件,download属性会根据浏览器对文件的支持而表现出不同。浏览器默认支持txt,json,pdf,image等文件,此时会直接预览打开,而不是下载。而对于不支持的文件,会直接下载,比如zip,xlsx,word.
<!--浏览器默认支持txt,json,markdown,pdf,image(png等)等文件的预览,如果是第三方资源,且不同源,此时a标签download的下载功能会失效;-->
<a href="http://storage.xuetangx.com/public_assets/xuetangx/PDF/PlayerAPI_v1.0.6.pdf" download>下载第三方PDF文档</a>
<a href="https://tse2-mm.cn.bing.net/th/id/OIP-C.D3eZ5Niid-sAEK0DHvSZPAHaKr?pid=ImgDet&rs=1" download>下载第三方图片</a>
2.1.2. 不能携带 Header
使用<a>标签下载是带不了Header 的,因此不能通过添加请求表头的形式来鉴权,但是可以将sessionid或token字段拼接到 URL 末尾来达到鉴权的目的。这里我们给出另一个解决方案:
- 先发送请求获取 blob 文件流,这样就能在请求时进行鉴权;
- 鉴权通过后再执行下载操作。
这样是不是就能很好的同时解决问题1和问题2带来的两个痛点了呢,而且下载的文件名也能自定义了
顺便提一下,location.href 和 window.open 也存在同样的问题。
2.2. 方式一:请求获取 blob 文件流下载
// 下载
export const fileDownload = (url, filename) => {
fetch(url)
.then(response => {
return response.blob()
})
.then(blob => {
const blobUrl = window.URL.createObjectURL(blob)
// 创建a标签下载
const tempLink = document.createElement('a')
tempLink.style.display = 'none'
tempLink.href = blobUrl
tempLink.setAttribute('download', filename)
document.body.appendChild(tempLink)
tempLink.click()
setTimeout(() => {
URL.revokeObjectURL(blobUrl)
tempLink.remove()
document.body.removeChild(tempLink)
})
})
}
2.3. 方式二:服务端设置响应标头 Content-Disposition为attachment
response.setHeader("Content-Disposition", "attachment;filename=" + filename);
2.3.1. Content-Disposition
在常规的 HTTP 应答中,Content-Disposition 响应标头指示回复的内容该以何种形式展示,是以内联的形式(即网页或者页面的一部分),还是以附件的形式下载并保存到本地。
Content-Disposition有两种属性值
- inline
默认值,即指明资源是直接展示在页面上的- attachment
即指明资源应该被下载到本地
2.3.2. download 与 Content-Disposition 的优先级
Content-Disposition设置后优先级高于a标签的download
- attachment > download > inline
- Content-Disposition的filename > download的值
833

被折叠的 条评论
为什么被折叠?



