前端下载篇

463 阅读3分钟

前言

下载文件是一个项目中非常常见的需求,在这里对多种前端下载方式总结一下。

方案一: window.open下载

适用于后端返回在线地址的情况,前端通过打开在线地址进行下载

window.open("http://a.dxiazaicc.com/down4/ldhtfw_downcc.com.zip");

优点:

  • 无兼容问题

缺点:

  • 无法打开浏览器支持的文件类型,例如pdfpngjpg
  • 有可能被拦截
  • 需要注意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')

优点:

  • 无兼容问题
  • 批量下载文件

缺点:

  • 无法打开浏览器支持的文件类型,例如pdfpngjpg
  • 不能添加添加header进行鉴权等
  • 无法获取下载的进度

方案三: a标签下载

通过a标签下载的方式,同window.open是一样的,非常简单,但也是和window.open一样,对于pdfpngjpg等文件不会直接下载,而是直接预览。所以a标签提供了download属性。

download属性是HTML5新增的属性,详情请点击

image.png

1. 静态a标签

用法:

<a href="example.jpg" download="test">点击下载</a>

优点: 可以下载同源的pdfpngjpg等可直接预览的文件 缺点:

  • 不能添加添加header进行鉴权等
  • 无法获取下载的进度
  • 不能下载跨域的pdfpngjpg等可直接预览的文件
  • download的兼容,详情可点击

2. 动态a标签

通常在业务开发中,文件通常是后端生成动态的下载。所以动态创建a标签下载更符合业务需求。 简而言之,创建一个 a 标签,添加 href 、 download 属性,模拟用户点击,实现下载

Blob

Blob URL / Object URL是一种伪协议,允许BlobFile对象用作图像,下载二进制数据链接等的URL源。

通过URL.createObjectURL(blob)方法创建Blob URL,将BlobFile类型的对象转为utf-16的字符串,这个URL的生命周期和创建它的窗口中的document绑定,保存在内存中。之后操作完成之后,需要通过URL.revokeObjectURL(objectURL)方法来释放内存

Base64

Base64属于Data URLs,即前缀为 data: 协议的URL,也可以通过a标签进行下载。

Data-URI的问题是每个charJavaScript中占用两个字节。最重要的是,由于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)
}

参考