下载远程文件并自定义默认下载名称


这个问题依旧是因为部门里那个温柔体贴疯狂找茬的测试提出的问题,指出下载默认名称不对,那有啥办法,测试大人都提出来了,改呗

情景再现:

  • Q:乍一看这个问题,下载不简单?直接往 a 标签的 href 属性塞个远程链接不就完事了?
  • W:但那样的话弹出下载框的默认名字是远程文件的名字耶,名字很乱的
  • Q:直接用download属性自定义下载默认名不就完事了?
  • w:这里就要说明一下这个download属性了:
    download是个h5新增属性,来规定文件的下载名称
    但这个方法有个问题,那就是只支持同源策略,跨域情况下这个属性是无效
    显然,在这里我们是远程文件,注定了一定会跨域,只能绕路选择其他办法了

方案一

思路

这里采取的是通过ajax请求远程文件获取文件数据的方式来处理,分为如下几步

  1. 将二进制数据转换为blob
    通过ajax请求远程文件url,获取返回值中的文件blob数据,即res.data
  2. 创建本地url
    根据blob对象,通过URL.createObjectURL(blob)API创建本地url,记住最后要通过URL.revokeObjectURL(url)释放 URL 地址
  3. 创建a标签
    创建a标签,根据本地url设置标签的href属性,并自定义设置其download属性,由于是本地url,为同源,因此download属性有效
  4. 模拟点击a标签下载文件
    将a标签添加入文档流中,通过click()模拟点击a标签,然后将其移除文档流就可以了
代码

引入需要类库,这里使用 axios 发出 ajax 请求

1
import axios from 'axios'

点击下载触发函数 downloadFile:

1
2
3
4
5
6
7
8
9
10
11
downloadFile(file)
{
axios.get(file.url, {
responseType: 'blob'
}).then(res => {
// 下载处理函数
downloadHandle(res)
}).catch(() => {
// 错误处理
})
}

请求处理函数 downloadHandle:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
downloadHandle(response, fileName = '')
{
// 提取文件名
if (!fileName) {
return
}
// 将二进制流转为blob
const blob = response.data
if (typeof window.navigator.msSaveBlob !== 'undefined') {
// 兼容IE,window.navigator.msSaveBlob:以本地方式保存文件
window.navigator.msSaveBlob(blob, decodeURI(fileName))
} else {
// 创建新的 URL 并指向 File 对象或者 Blob 对象的地址
const blobURL = window.URL.createObjectURL(blob)
// 创建 a 标签,用于跳转至下载链接
const tempLink = document.createElement('a')
tempLink.style.display = 'none'
tempLink.href = blobURL
// 设置 download 属性,自定义文件名称
tempLink.setAttribute('download', decodeURI(fileName))
// 兼容:某些浏览器不支持HTML5的download属性
if (typeof tempLink.download === 'undefined') {
tempLink.setAttribute('target', '_blank')
}
// 挂载 a 标签
document.body.appendChild(tempLink)
// 模拟点击 a 标签
tempLink.click()
// 移除 a 标签
document.body.removeChild(tempLink)
// 释放blob URL地址
window.URL.revokeObjectURL(blobURL)
}
}

方案二

  • q:上面这些好麻烦啊,又不想努力,只能找找类库两行代码解决才能维持的了生计这个样子
  • w:那可以试试这个插件:FileSaver.js
FileSaver.js

官方文档

  1. 安装
1
npm install file-saver --save
  1. 引用
1
import FileSaver from 'file-saver'
  1. 使用
1
FileSaver.saveAs(Blob / File / Url, filename)

使用 FileSaver.js 实现问题需求

1
2
3
4
5
6
import FileSaver from 'file-saver'

// 点击下载触发函数
function downloadFile(file) {
FileSaver.saveAs(file.url, file.name)
}

一行搞定,喜大普奔

后记

不过个人还是建议学会或者直接使用方案一中的方法,毕竟使用类库会增大项目大小,而且本质也不麻烦,程序员本来就是需要不断地学习,一味使用别人插件那不就是搬砖工了吗其实我只是懒得学各种类库插件啊哈哈