实现JS监听文件下载完成的功能(解决不同浏览器的下载触发 + a标签base64url过长无法下载的问题)

一般来说,我们的js文件下载会使用window.location.href定位到后端的接口,后端生成文件返回,然后浏览器自动下载。这种方法最简单,但是无法获取下载成功的通知,在大文件生成和下载的时候,时间过长,用户可能会重复进行下载的点击,对服务器造成负担。

因此要用另外的方式发送请求来监听文件下载完成,此方法使用的是XMLHttpRequest来请求,可以监听文件下载完成,另外一提,如果要对下载使用进度条监听的话,可以使用设置cookie循环向后端请求获取进度的方法,本文只讨论前者。

不多bb,直接开始

这是我一开始的版本:

load = function() {
    
    
//填写你的下载时加载的提示
}
disload = function() {
    
    
//下载完成后触发,用来关闭提示框
}
getDownload = function(url) {
    
    
     load();
     var xhr = new XMLHttpRequest();
     xhr.open('GET', url, true);    // 也可用POST方式
     xhr.responseType = "blob";
     xhr.onload = function () {
    
    
         if (this.status === 200) {
    
    
             var blob = this.response;
             var reader = new FileReader();
             reader.readAsDataURL(blob);
             reader.onload = function (e) {
    
    
                 var headerName = xhr.getResponseHeader("Content-disposition");
                 var fileName = decodeURIComponent(headerName).substring(20);
                     var a = document.createElement('a');
                     a.download = fileName + ".xls";
                     a.href = e.target.result;
                     $("body").append(a);    // 修复firefox中无法触发click
                     a.click();
                     $(a).remove();
             };
             disload();
         }
     };
     xhr.send()
 };

这个原理是通过XMLHttpRequest发送请求,利用FileReader来读取文件内容,关键是设置a标签,将blob的文件内容转换为base64并放入a标签的href中,模拟点击来进行下载。

但是需要注意的是

  1. a标签的download经测试,仅在谷歌和火狐浏览器生效,在edge和IE下不触发下载
  2. 如果以base64为a标签的url地址的话,由于长度限制最多只能下载大约2M的文件,再高的话就会下载失败

第一个问题

对于ie等浏览器来说,有另外的一种方法来触发下载。

 function dataURLtoBlob(dataurl) {
    
    
            var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1],
                bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
            while (n--) {
    
    
                u8arr[n] = bstr.charCodeAt(n);
            }
            return new Blob([u8arr], {
    
     type: mime });
        }
        
//e.target.result 正是上面代码中转换后的baseUrl,这种方法要转换为blob
var blob = dataURLtoBlob(e.target.result);
navigator.msSaveBlob(blob, fileName + ".xls");

需要注意的是navigator.msSaveBlob在谷歌等浏览器中并不存在,在此,就解决了第一个问题

第二个问题

第二个问题其实也看需求,对于非常小的文件,比如小图标等可以用base64作为url地址,对于其他的大文件,可以将文件存入内存中,用虚拟内存地址指向。

var blob = dataURLtoBlob(e.target.result);
var a = document.createElement('a');
a.download = fileName + ".xls";
a.href = URL.createObjectURL(blob);
$("body").append(a);    // 修复firefox中无法触发click
a.click();
URL.revokeObjectURL(a.href);
$(a).remove();

URL.createObjectURL 将blob形式的文件存入并返回一个url以供下载。
URL.revokeObjectURL则将该内存释放。
至此,两个问题解决,可以看到大文件根本不能使用base64,直接使用blob是最好的。因此合并起来就可以得到最终版本了:

最终版本

load = function() {
    
    
//填写你的下载时加载的提示
}
disload = function() {
    
    
//下载完成后触发,用来关闭提示框
}
getDownload = function(url) {
    
    
	load();
    var xhr = new XMLHttpRequest();
    xhr.open('GET', url, true);    // 也可用POST方式
    xhr.responseType = "blob";
    xhr.onload = function () {
    
    
        if (this.status === 200) {
    
    
            var blob = this.response;
            if (navigator.msSaveBlob == null) {
    
    
                var a = document.createElement('a');
                var headerName = xhr.getResponseHeader("Content-disposition");
                var fileName = decodeURIComponent(headerName).substring(20);
                a.download = fileName;
                a.href = URL.createObjectURL(blob);
                $("body").append(a);    // 修复firefox中无法触发click
                a.click();
                URL.revokeObjectURL(a.href);
                $(a).remove();
            } else {
    
    
                navigator.msSaveBlob(blob, fileName);
            }
        }
        disload();
    };
    xhr.send()
};

猜你喜欢

转载自blog.csdn.net/qq_35530005/article/details/103381205