遇到的场景,后端通过接口返回给前端一个下载地址,这个地址可以直接放到浏览器地址栏直接下载,也可以通过 a 标签的 href 赋值,然后点击 a 标签下载。目前的问题是需要对它进行重命名,a 标签的 download 属性是不生效的。 解决方案:通过 xhr get 方式获取该资源的 blob 格式数据,再通过window.URL.createObjectURL 转为 a 的 href,这个时候设置的 download 属性会生效
问题
文件保存在 OSS,为了避免文件重名覆盖,于是上传文件采用了 UUID 命名(嗯,就是一长串ABCD那种O_O`)
但是,下载的时候:
- 这个是啥...
- 你上传的配置文件啊
- 我明明传的是
xxx.txt
,怎么变成F8581FA0D-94D0-4DE4-98B0-F3416AEF0BB9.txt
了,什么鬼 - 那个是 OSS 为了...
- 我不听我不听!!
- 哦...那我改改
坑
网上搜呗,HTML5 中 a
标签提供了一个 filename
属性,可以下载成指定的 download
属性名称。嗯,官网原意是这样的。
<a href="/images/myw3schoolimage.jpg" download="w3logo">
官网 demo 是这样子的,试了一下,是 OK 的,不信你去看看。
但是,但是呢,这个搞 OSS 的链接不行,设置 download 无效...
但是,这边还有一个但是,骚年,你这个 IE 他不支持啊,不支持啊,不支持啊!! 亲,快醒醒!天要亮了 = _=
解决方法
上知乎看看,有插件 eligrey/FileSaver.js · GitHub
差不多就是获取文件 Blob,然后下载重命名,简单整理了一下:
/**
* 获取 blob
* @param {String} url 目标文件地址
* @return {Promise}
*/
function getBlob(url) {
return new Promise(resolve => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = () => {
if (xhr.status === 200) {
resolve(xhr.response);
}
};
xhr.send();
});
}
/**
* 保存
* @param {Blob} blob
* @param {String} filename 想要保存的文件名称
*/
function saveAs(blob, filename) {
if (window.navigator.msSaveOrOpenBlob) {
navigator.msSaveBlob(blob, filename);
} else {
const link = document.createElement('a');
const body = document.querySelector('body');
link.href = window.URL.createObjectURL(blob);
link.download = filename;
// fix Firefox
link.style.display = 'none';
body.appendChild(link);
link.click();
body.removeChild(link);
window.URL.revokeObjectURL(link.href);
}
}
/**
* 下载
* @param {String} url 目标文件地址
* @param {String} filename 想要保存的文件名称
*/
function download(url, filename) {
getBlob(url).then(blob => {
saveAs(blob, filename);
});
}
来一发(看不见?粘到控制台试试):
download('https://github.com/vuejs/vue-router', 'vue-router.html');
缺陷
缺陷必然会有,自行取舍吧= =
- 非同源会有跨域问题
可以设置资源允许跨域,如果可以设置的话
- 会预先加载(下载)文件,如果文件比较大,等待时间会比较长
可以设置 loading 方式解决,不过好像也不是很完美
兼容性
Chrome
IE10+
示例有
ES6
的语法,请自行转一下