谈到文件下载,相信绝大多数的人都会想到a标签
比如,W3school的一个基础案例
<a href="/images/myw3schoolimage.jpg" download="w3logo">
我们可以用download来修改下载的文件名
然而一切并不是那么简单
1.跨域的时候利用download去修改文件名并不生效
2.当href指向为图片的时候,并不会下载图片,而是执行了预览图片
github上有现有的资源可以使用 fileSaver.js
其实逻辑非常简单,提取了这一小部分代码
/**
* 获取 blob
* @param {String} url 目标文件地址
* @return {Promise}
*/
function getBlob(url) {
return new Promise(resolve => {
const xhr = new XMLHttpRequest()
// 避免 200 from disk cache
url = url + `?r=${Math.random()}`
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 anchor = document.createElement('a')
const body = document.querySelector('body')
anchor.href = window.URL.createObjectURL(blob)
anchor.download = filename
anchor.style.display = 'none'
body.appendChild(anchor)
anchor.click()
body.removeChild(anchor)
window.URL.revokeObjectURL(anchor.href)
}
}
/**
* 下载
* @param {String} url 目标文件地址
* @param {String} newFilename 想要保存的文件名称
*/
export async function download(url, newFilename) {
const blob = await getBlob(url)
saveAs(blob, newFilename)
}
当我们使用这个简化版的时候,我们必须是对下载的域名可以进行访问的,也就是说我们的xhr对象不会被浏览器的同源策略给影响到。
源码有对服务器端是否允许该域名进行校验,具体代码如下:
if (corsEnabled(blob)) {
download(blob, name, opts)
} else {
var a = document.createElement('a')
a.href = blob
a.target = '_blank'
setTimeout(function () {
click(a)
})
}
function corsEnabled(url) {
var xhr = new XMLHttpRequest() // use sync to avoid popup blocker
xhr.open('HEAD', url, false)
xhr.send()
return xhr.status >= 200 && xhr.status <= 299
}
这个可以直接看源码,源码也是非常清晰的。