问题背景:
在使用a标签下载文件时,download属性可以更改下载的文件名。
// 下载a.exe,并采用默认命名
<a href="/images/a.exe" download>点击下载</a>
// 将a.exe改名为b.exe下载
<a href="/images/a.exe" download="b">点击下载</a>
但是当a标签的下载链接跨域时,download属性将不会生效,原因是浏览器无法获取到文件,不能对他进行更改。
在这种情况下如果你是下载浏览器无法解析的文件,例如.exe,.xlsx..那么浏览器也会自动下载,但是如果你使用浏览器可以解析的文件,比如.txt,.png....浏览器就会采取预览模式,无法直接下载。
// 无法正常下载a.jpg,会直接预览
<a href="https://www.baidu.com/a.jpg" download>点击下载</a>
// 正常下载a.exe文件
<a href="https://www.baidu.com/a.exe" download>点击下载</a>
注意:
html5 新特性a标签download属性只支持谷歌和火狐
在谷歌和火狐浏览器a标签download属性修改文件名失效的原因:不同源,访问的域名和href的域名要一致。
a标签属性文档:<a>:锚元素 - HTML(超文本标记语言) | MDN
什么是同源url:
解决方法:
使用Blob实现文件下载,先把文件以bobl的形式下载到当前页面,再创建a标签。
// 下载url(解决跨域a.download不生效问题)
downloadFile(url, fileName) {
const x = new XMLHttpRequest()
x.open("GET", url, true)
x.responseType = 'blob'
x.onload = function(e) {
const url = window.URL.createObjectURL(x.response)
const a = document.createElement('a')
a.href = url
a.target = '_blank'
a.download = fileName
a.click()
a.remove()
}
x.send()
},
使用方法:
<el-link
:disabled="validateNull(fileUrl)"
:type="validateNull(fileUrl) ? 'info' : 'primary'"
:underline="true"
style="font-size:14px;"
@click="downloadFile(fileUrl, fileName)"
>
{{ fileName|| '-' }}
</el-link>
这里的validateNull是校验url是否为空:
/**
* 判断是否为空
*/
export function validatenull(val) {
if (typeof val === 'boolean') {
return false;
}
if (typeof val === 'number') {
return false;
}
if (val instanceof Array) {
if (val.length===0) return true;
} else if (val instanceof Object) {
if (JSON.stringify(val) === '{}') return true;
} else {
if (val==='null' || val===null || val==='undefined' || val===undefined || val==='') return true;
return false;
}
return false;
}
这个下载方法有个需要注意的问题就是,当自定义的fileName内存在小数点时(如 fileName = '附件v1.2.zip'),如果没有在fileName后面加上后缀名(fileName = '附件v1.2'),会导致下载时自动以第一个小数点后面的为文件后缀名(.2),所以这时还要加一个方法判断文件的后缀名,将后缀名加到自定义的文件名后面:
function getFileTypeByPath(pathUrl) {
let index = pathUrl.lastIndexOf(".") // lastIndexOf(".") 找到最后一个 . 的位置
let fileType = pathUrl.substr(index + 1) // substr() 截取剩余的字符,即文件后缀名 doc 、jpg 、 xlsx 等
return fileType
}
此时我们传入的fileName为:
const suffix = this.getFileTypeByPath(fileUrl)
this.suffix = `.${suffix}`
const fileName = '附件v1.2'
this.fileName = `${fileName}${this.suffix}`