同源和不同源的文件下载解析

一、什么是同源和不同源文件

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 的,因此不能通过添加请求表头的形式来鉴权,但是可以将sessionidtoken字段拼接到 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的值
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值