目前前端下载文件的最佳实践
目前前端最佳的方案其实有两种:
- a元素下载
- iframe 无闪下载
事实上这两种下载方式,其实都存在相同的一个问题,就是如果下载的文件是跨域的(不是同域名下的问题,或者其他原因);并且这个文件浏览器可以直接打开的,比如:图片、文本文件。
那么浏览器会直接打开文件,并不是触发下载,而如果下载的文件是安装包,压缩包zip这些,则是触发下载。
这个问题就很头疼。
a元素下载的缺点
a元素下载,那么就必须设置download属性,这个属性可以接收一个参数,可以是带后缀的文件名,或者不带后缀。也可以为空,但是要设。
不带后缀的话文件格式就会与下载的文件保持一致,如果带了格式后缀,可能导致下载的jpg文件,你设置为xxx.png
,那么就会有格式上的问题。
而这个download其实是有兼容性的问题的,网传safari浏览器并不支持这个属性,其实就现在而言,其实是支持的,不支持的是ie。
然后就是文件预览的问题的,这个上面已经说明了。
iframe下载的缺点
iframe也会有预览的问题,但是没有兼容问题,由于可以预览,iframe一般都是display:none;
,所以即便预览了,我们也不知道,所以这种方式一般用于下载压缩包,安装包,浏览器无法直接预览的文件。
他不同于a链接,a链接的href属性不一定非得是外链,可以是BlobUrl和DataUrl。所以,a链接可以防止文件被预览,iframe则不行。
a链接如何防止文件被预览
两种方式:
- BlobUrl
- DataUrl
BlobUrl
BlobUrl就是通过请求,如ajax,或者fetch获取到文件对象,将其转为blob对象。
然后通过这个blob对象创建url,然后赋值给a链接即可。
//假设b等于blob对象
let a = document.createElement("a");
a.href = URL.createObjectURL(b);
a.download = fileName;
a.style.display = "none";
document.body.appendChild(a);
a.click();
URL.revokeObjectURL(a.href); // 释放URL 对象
a.remove();
这一套就完事了。
而且对于blob对象,和fetch合作简直就是天作之合,fetch可以直接将下载的文件转为blob。
function fileDownload(href, fileName = "") {
return new Promise((resolve, reject) => {
if (!href) return Promise.reject("herf文件下载地址不能为空");
fetch(href, {method: "GET",}).then(response => {
if (response.ok) {
return response.blob(); //转bolb
} else {
return reject(response.statusText);
}
}).then(blob => {
let a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = fileName;
a.style.display = "none";
document.body.appendChild(a);
a.click();
URL.revokeObjectURL(a.href); // 释放URL 对象
a.remove();
return resolve(true);
}).catch(err => {
return reject(err);
});
});
};
结合一下,万精油,啥文件都给你干下来。
DataUrl
DataUrl实际上就是base64,既然是base64,那么就绕不过canvas转base64,所以我就很烦这个,因为容易产生跨域问题,这个我暂时也没研究,因为感觉不太好用,就放一个别人的代码吧!
// ./util.js
// 图片转base64
function image2base64(img) {
const canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, img.width, img.height);
const mime = img.src.substring(img.src.lastIndexOf(".")+1).toLowerCase();
const dataUrl = canvas.toDataURL("image/" + mime);
return dataUrl;
}
// html页面,将a标签href属性动态赋值为dataUrl
<a id='downloadDataUrl' class="button is-dark">下载data:Url图片</a>
...
<script>
const image = new Image();
image.setAttribute("crossOrigin",'Anonymous');
image.src = '../files/test-download.png' + '?' + new Date().getTime();
image.onload = function() {
const imageDataUrl = image2base64(image);
const downloadDataUrlDom = document.getElementById('downloadDataUrl');
downloadDataUrlDom.setAttribute('href', imageDataUrl);
downloadDataUrlDom.setAttribute('download', 'download-data-url.png');
downloadDataUrlDom.addEventListener('click', () => {
console.log('下载文件');
});
}
</script>
使用这两种方式可以下载。iframe的也参考上面那个出处吧,等以后用到了我再贴代码。
第三方下载库 FileSaver
有现成的轮子不香吗????
这个库下载小文件可以,如果超大文件,超出了限制,就需要换个库了,具体看这个轮子的说明文档。
本文系作者 @木灵鱼儿 原创发布在木灵鱼儿站点。未经许可,禁止转载。
暂无评论数据