前段時間遇到一個需求,需要在H5頁面中增加下載文件按鈕,下載一個壓縮文件,第一個想到的當然是最普遍和常用的方法:動態生成a標籤,把鏈接地址給到href屬性,觸發一個click事件,完美下載。於是擼起袖子就開幹。
由於文件數據是通過接口請求回來的數據流,application/octet-stream類型,需要轉換一下來使用:
download() {
let blob = res.data;
// 請求文件數據,返回類型爲blob數據流,類型application/octet-stream
let url = window.URL.createObjectURL(blob); // 將blob轉換爲數據連接地址
let a = document.createElement('a');
a.download = 'test.zip';
a.href = url;
a.target = '_blank';
document.appendChild(a);
a.click();
document.removeChild(a);
}
在pc上調試時很好,下載沒有任何問題,但是放到真機上就一堆問題來了:
iOS系統點擊就沒反應,根本沒法下載,安卓手機也是幾乎所有瀏覽器都有問題,要麼下載的文件後綴不對,要麼沒法下載,全軍覆沒。
網上查了下,H5下載文件失敗幾乎是普遍問題,只有圖片是沒有兼容性問題的,長按保存本地,但是文件很多頁的時候就不現實了,幾乎沒有什麼體驗可言。還有的是說ios是極度封閉的環境,下載東西很麻煩,不好處理。
於是又開始嘗試其它辦法
1、file-saver插件
npm i file-saver -S
使用:
import fileSaver from 'file-saver';
、、、
let blob = res.data;
// 請求文件數據,返回類型爲blob數據流,類型application/octet-stream
let url = window.URL.createObjectURL(blob); // 將blob轉換爲數據連接地址
fileSaver.saveAs(url,'test.zip');
結果證明效果是一樣的,真機上下載不了。
2、file-saver + jszip
安裝jszip:
npm i jszip -S
使用:
import jsZip from 'jszip';
import { saveAs } from 'file-saver';
、、、
let zip = new jsZip();
var myFile = zip.folder(); // 新建一個文件夾
let fileReader = new FileReader();
fileReader.onload = function(e) {
let base64 = e.target.result;
myFile.file('test.zip', base64, {
base64: true
}); // 文件名稱
zip.generateAsync({ type: 'blob' }).then(function(content) {
// see FileSaver.js
saveAs(content, 'test.zip');
});
}
fileReader.readAsDataURL(blob);
效果依舊、、、失敗
3、直接嘗試着使用絕對地址來做測試,直接下載一個服務器上的壓縮文件
download() {
let url = htts://xxxxxx/zzz.zip; // 絕對地址
let a = document.createElement('a');
a.download = 'test.zip';
a.href = url;
a.target = '_blank';
document.appendChild(a);
a.click();
document.removeChild(a);
}
真機調試,毫無兼容性問題,安卓和ios的各個瀏覽器都沒問題,只有uc瀏覽器不支持打開zip文件而彈出提示,無法下載,所以思路就有了:
讓後臺把文件壓縮好,生成一個絕對路徑地址,然後把路徑給到前臺下載。
方法有兩種:
1、文件放到七牛雲上,生成絕對地址下載,但是這地址沒加權限,任何人拿到都可以下載,對於合同文件類型的資源來說不夠安全,放棄。
2、放到自己服務器上,依然生成絕對地址,給鏈接地址設置時效性,超過時間則清空服務器資源,使得下載鏈接無效,也可以很好的節約文件服務器資源。
3、放到自己服務器上,給鏈接做權限校驗,前端根據後臺給到的規則拼接地址,最後加上token作爲權限校驗
由於這個項目做了三級等保功能,討論後決定使用第3種方案,和三級等保無縫切合。
代碼:
download() {
let url = `${location.origin}/xxxx/${this.fileCode}?token=${this.token}`; // 拼接下載地址
let a = document.createElement('a');
a.download = 'test.zip';
a.href = url;
a.target = '_blank';
document.appendChild(a);
a.click();
document.removeChild(a);
}
這個方法雖然不能說完美,但至少能兼容絕大部分主流瀏覽器和移動端操作系統了,基本上能滿足大部分項目的需求。