前言
下载文件是一个项目中非常常见的需求,在这里对多种前端下载方式总结一下。
方案一: window.open下载
适用于后端返回在线地址的情况,前端通过打开在线地址进行下载
window.open("http://a.dxiazaicc.com/down4/ldhtfw_downcc.com.zip");
优点:
- 无兼容问题
缺点:
- 无法打开浏览器支持的文件类型,例如
pdf、png、jpg等 - 有可能被拦截
- 需要注意
URL长度和编码 - 不能添加添加
header进行鉴权等 - 无法获取下载的进度
方案二: iframe下载
function iFrameDownload (url) {
const iframe = document.createElement('iframe');
iframe.id = `iframe-${url}`;
iframe.style.display = 'none';
function iframeLoad() {
document.body.removeChild(iframe);
}
if ('onload' in iframe) {
iframe.onload = iframeLoad;
} else if (iframe.attachEvent) {
iframe.attachEvent('onload', iframeLoad);
} else {
iframe.onreadystatechange = function onreadystatechange() {
if (iframe.readyState === 'complete') {
iframeLoad();
}
};
}
iframe.src = url;
document.body.appendChild(iframe);
}
iFrameDownload('https://down.2ppt.com/down/0ad81ded-de65-4c5d-ac11-e8cd82d51f95.pptx')
优点:
- 无兼容问题
- 批量下载文件
缺点:
- 无法打开浏览器支持的文件类型,例如
pdf、png、jpg等 - 不能添加添加
header进行鉴权等 - 无法获取下载的进度
方案三: a标签下载
通过a标签下载的方式,同window.open是一样的,非常简单,但也是和window.open一样,对于pdf、png、jpg等文件不会直接下载,而是直接预览。所以a标签提供了download属性。
download属性是HTML5新增的属性,详情请点击
1. 静态a标签
用法:
<a href="example.jpg" download="test">点击下载</a>
优点:
可以下载同源的pdf、png、jpg等可直接预览的文件
缺点:
- 不能添加添加
header进行鉴权等 - 无法获取下载的进度
- 不能下载跨域的
pdf、png、jpg等可直接预览的文件 download的兼容,详情可点击
2. 动态a标签
通常在业务开发中,文件通常是后端生成动态的下载。所以动态创建a标签下载更符合业务需求。
简而言之,创建一个 a 标签,添加 href 、 download 属性,模拟用户点击,实现下载
Blob
Blob URL/Object URL是一种伪协议,允许Blob和File对象用作图像,下载二进制数据链接等的URL源。通过
URL.createObjectURL(blob)方法创建Blob URL,将Blob或File类型的对象转为utf-16的字符串,这个URL的生命周期和创建它的窗口中的document绑定,保存在内存中。之后操作完成之后,需要通过URL.revokeObjectURL(objectURL)方法来释放内存
Base64
Base64属于Data URLs,即前缀为data:协议的URL,也可以通过a标签进行下载。
Data-URI的问题是每个char在JavaScript中占用两个字节。最重要的是,由于Base-64编码增加了33%。Blob是纯粹的二进制字节数组,它不像Data-URI那样具有任何重要的开销,这使得它们处理速度越来越快。
下载ArrayBuffer数据
后端返回ArrayBuffer,我们需要先将ArrayBuffer转成Blob,之后生成Blob URL进行下载。
// 模拟后端返回 ArrayBuffer
const str = "hello world!";
let ab = new ArrayBuffer(str.length);
let ia = new Uint8Array(ab);
for (let i = 0; i < str.length; i++) {
ia[i] = str.charCodeAt(i);
}
function arrarBufferDownload(buffer, type, filename) {
// 将 ArrayBuffer 转成blob,这里也可以转成file对象。
const blob = new Blob([buffer], { type });
const link = document.createElement('a');
// 设置文件名
link.download = filename || ''
// 直接生成blob url。这里可以使用Blob对象或者File对象
const url = window.URL.createObjectURL(blobData);
link.href = url;
document.body.appendChild(link);
link.click();
// 释放内存
window.URL.revokeObjectURL(url);
// 移除a元素
document.body.removeChild(link);
}
arrarBufferDownload(ia, "text/plain", 'test')
下载Blob
直接返回Blob的话,那么可以直接使用URL.createObjectURL(blob)方法创建Blob URL,之后将Blob URL复制给a标签,最后模拟点击a标签完成下载。
const getBlob = (url) => {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'blob';
xhr.onload = () => {
if (xhr.status === 200) {
resolve(xhr.response);
}
};
xhr.onerror = function () {
reject();
};
xhr.send();
});
};
async function blobDownload(url) {
const blobData = await getBlob(url);
const link = document.createElement('a');
// 设置文件名
filename && (link.download = filename);
// 直接生成blob url。这里可以使用Blob对象或者File对象
const url = window.URL.createObjectURL(blobData);
link.href = url;
document.body.appendChild(link);
link.click();
// 释放内存
window.URL.revokeObjectURL(url);
// 移除a元素
document.body.removeChild(link);
}
blobDownload('https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2F4k%2Fs%2F02%2F2109242306111155-0-lp.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80')
下载Base64
后端直接返回Base64的情况不多见,不过我们还是可以使用a标签进行下载。
function alinkDownload(url, filename = '') {
const link = document.createElement('a');
filename && (link.download = filename);
link.href = url;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
function base64Download(filename) {
// 这里就是将本地的图片转为base64
const b1 = await img2base64("./imgs/logo.png");
alinkDownload(b1)
}