木灵鱼儿

木灵鱼儿

阅读:257

最后更新:2021/05/08/ 11:20:15

目前前端下载文件的最佳实践

目前前端最佳的方案其实有两种:

  1. a元素下载
  2. iframe 无闪下载

事实上这两种下载方式,其实都存在相同的一个问题,就是如果下载的文件是跨域的(不是同域名下的问题,或者其他原因);并且这个文件浏览器可以直接打开的,比如:图片、文本文件。

那么浏览器会直接打开文件,并不是触发下载,而如果下载的文件是安装包,压缩包zip这些,则是触发下载。

这个问题就很头疼。

a元素下载的缺点

a元素下载,那么就必须设置download属性,这个属性可以接收一个参数,可以是带后缀的文件名,或者不带后缀。也可以为空,但是要设。

不带后缀的话文件格式就会与下载的文件保持一致,如果带了格式后缀,可能导致下载的jpg文件,你设置为xxx.png,那么就会有格式上的问题。

而这个download其实是有兼容性的问题的,网传safari浏览器并不支持这个属性,其实就现在而言,其实是支持的,不支持的是ie。

然后就是文件预览的问题的,这个上面已经说明了。

iframe下载的缺点

iframe也会有预览的问题,但是没有兼容问题,由于可以预览,iframe一般都是display:none;,所以即便预览了,我们也不知道,所以这种方式一般用于下载压缩包,安装包,浏览器无法直接预览的文件。

他不同于a链接,a链接的href属性不一定非得是外链,可以是BlobUrl和DataUrl。所以,a链接可以防止文件被预览,iframe则不行。

a链接如何防止文件被预览

两种方式:

  1. BlobUrl
  2. 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

有现成的轮子不香吗????

FileSaver.js

这个库下载小文件可以,如果超大文件,超出了限制,就需要换个库了,具体看这个轮子的说明文档。

版权申明

本文系作者 @木灵鱼儿 原创发布在木灵鱼儿 - 有梦就能远航站点。未经许可,禁止转载。

关于作者

站点职位 博主
获得点赞 0
文章被阅读 257

相关文章