javascript 實現原生下載的各種情況


還記得第一次做文檔下載的時候,基於windows.open('download_url')的方式下載。在某天某月某日,有領導突然review我的代碼,此種方式遭到吐槽,尷尬不已。才痛下決心決定梳理一下前台的下載功能

windows.open缺點 1.用戶交互不友好 2.對於圖片類型的文件,會直接打開瀏覽器,而不是下載等等,自己領悟

下方的方式很多,請根據實際業務需求動態選擇

1:一般通過a標簽的方式下載,利用H5的Download屬性

代碼示例如下:

場景:適用於現代瀏覽器,url是下載地址,而不是文件流,常用於GET請求

 1 function downLoad(downUrl, fileName) {
 2   let a = document.createElement("a");// 創建a標簽
 3   if ('download' in a) {
 4     a.download = fileName;// 設置下載文件的文件名
 5   }
 6   (document.body || document.documentElement).appendChild(a);
 7   a.href = downUrl;// downUrl為后台返回的下載地址
 8   a.target = '_parent';
 9   a.click();// 設置點擊事件
10   a.remove(); // 移除a標簽
11 }
12 
13 downLoad(URL, 'test.xlxs') //URL下載地址

 

如果后台給的是文件流,則利於Blob對象包裝一下文件流,常用於POST請求

代碼示例如下:

 1 function downLoad(content, fileName) {
 2   let a = document.createElement("a"),// 創建a標簽
 3       blob = new Blob([content]);  //用Blob對象包裝一下文件流
 4 
 5   if ('download' in a) {
 6     a.download = fileName;// 設置下載文件的文件名
 7   }
 8   (document.body || document.documentElement).appendChild(a);
 9   a.href = window.URL.createObjectUrl(blob);;// content為后台返回的文件流
10   a.click();// 設置點擊事件
11   a.remove(); // 移除a標簽
12 }
13 downLoad('我是文件流我是文件流', 'test.txt')

如果是POST請求,可以借助axios實現,配合FileReader對象。

 1 axios.post(downloadUrl, this.searchParams, {
 2   responseType: 'blob'
 3 }).then(res => {
 4   const blob = res.data
 5   const reader = new FileReader()
 6   reader.readAsDataURL(blob)
 7   reader.onload = (e) => {
 8     const a = document.createElement('a')
 9     a.download = `表格名稱.xlsx`
10     a.href = e.target.result
11     document.body.appendChild(a)
12     a.click()
13     document.body.removeChild(a)
14   }
15 }).catch(err => {
16   console.log(err.message)
17 })

2:借助Base64實現文件的下載

對於非文本文件,也是可以借助JS下載的,比如下載圖片,前端直接可以轉化為base64然后下載

直接上實例代碼吧,應該都看得懂,我就不多說了,

代碼來自張鑫旭的關於下載的博文,可以直接查看

 1 function download (domImg, filename) {
 2   // 創建隱藏的可下載鏈接
 3   let eleLink = document.createElement('a');
 4   eleLink.download = filename;
 5   eleLink.style.display = 'none';
 6   // 圖片轉base64地址
 7   let canvas = document.createElement('canvas');
 8   let context = canvas.getContext('2d');
 9   let width = domImg.naturalWidth;
10   let height = domImg.naturalHeight;
11   context.drawImage(domImg, 0, 0);
12   // 如果是PNG圖片,則canvas.toDataURL('image/png')
13   eleLink.href = canvas.toDataURL('image/jpeg');
14   // 觸發點擊
15   document.body.appendChild(eleLink);
16   eleLink.click();
17   // 然后移除
18   document.body.removeChild(eleLink);
19 };

 3:通過formData標簽的方式下載,這是平安易建產品所使用的下載方式,兼容低版本瀏覽器

直接上代碼,因為涉及到加簽驗簽和平安的實際業務,所以看不懂就不用看了,直接上代碼,用於自己以后參考

 1 function downfile(file) {
 2   if (file == undefined || file == null) {
 3     return;
 4   }
 5   const nonce = Math.floor(Math.random() * (Math.floor(100) - Math.ceil(1))) + Math.ceil(1);
 6   const timestamp = parseInt(Date.now() / 1000);
 7   const searchParams = {
 8     storageKey: file.storageKey, // 文件系統文件key
 9     tokenType: 2, // 申請token的類型,1:上傳,2:下載,5:預覽
10     nonce,
11     timestamp,
12     alg: 'MD5',
13   };
14   axios({ url: '/docs/api/file/token/applyToken', method: 'get', params: searchParams })
15     .then(res => {
16       if (!res.status === 200 || !res.data) {
17         return;
18       } if (!res.data.success) {
19         const msg = res.data.msg && res.data.msg.length > 0 ? res.data.msg : '下載失敗!';
20         message.error(msg);
21 
22       } else {
23         res = res.data || {};
24         let content = {};
25         if (res.data.contentList && res.data.contentList.length > 0) {
26           content = res.data.contentList[0];
27         }
28         return {
29           nodeUrl: content.nodeUrl,
30           token: content.token
31         }
32       }
33     }).then(({ nodeUrl, token }) => {
34       const url = `${nodeUrl}/node/download/view/${file.storageKey}`; // 文件下載路徑
35       const form = document.createElement('form');
36       form.setAttribute("method", "get");
37       form.setAttribute("name", "theForm");
38       form.setAttribute("target", "_self");
39       form.setAttribute('style', 'display:none');
40       form.setAttribute("action", url);
41       document.body.appendChild(form);
42 
43       const newinput_fileToken = document.createElement('input');
44       newinput_fileToken.setAttribute('type', 'hidden');
45       newinput_fileToken.setAttribute('name', 'fileToken');
46       newinput_fileToken.setAttribute('value', token)
47 
48       const newinput_fileName = document.createElement('input');
49       newinput_fileName.setAttribute('type', 'hidden');
50       newinput_fileName.setAttribute('name', 'fileName');
51       newinput_fileName.setAttribute('value', file.name)
52 
53       form.appendChild(newinput_fileToken);
54       form.appendChild(newinput_fileName);
55 
56       form.submit();
57       document.theForm.parentNode.removeChild(form);
58     });
59 }

另附折疊版的批量下載,實際上差不多一樣,如果以后實際項目中遇到,可以綜合在一起

 1 function batchDownfile(files, useType) {
 2   if(files == undefined || files == null || files.length == 0){
 3     return;
 4   }
 5   let applyTokenForms = [];
 6   const hasDoc = files.some(file => file.type == 1)
 7   if(hasDoc){
 8     applyTokenForms = files.map(it => ({
 9       fileId: it.id
10     }))
11   } else {
12     applyTokenForms = files.map(it => ({
13       storageKey: it.storageKey,
14       fileName: it.name
15     }))
16   }
17   const param = hasDoc ? {
18     useType: 11
19   } : {}
20   if(useType){
21     param.useType = useType
22   }
23   const nonce = Math.floor(Math.random()*(Math.floor(100)-Math.ceil(1)))+Math.ceil(1);
24   const timestamp = parseInt(Date.now()/1000);
25   const searchParams = {
26     ...param,
27     applyTokenForms,
28     tokenType: 4, // 申請token的類型,1:上傳,2:下載,4, 批量下載, 5:預覽
29     nonce,
30     timestamp,
31     alg:'MD5',
32   }; 
33   const newParam =  setParamSign(JSON.parse(JSON.stringify(searchParams)));
34   axios.post(`/docs/api/file/token/applyTokens`,parse(newParam))
35     .then(res => {
36       if (!res.status === 200 || !res.data){ 
37         return; 
38       }if(!res.data.success){
39         const msg = res.data.msg && res.data.msg.length > 0 ? res.data.msg : '下載失敗!';
40         message.error(msg);  
41               
42       }else{
43         res = res.data || {};
44         let fileList = [];
45         let nodeUrl = '';
46         let zipName = '';
47         if(res.data && res.data.length > 0){
48           fileList = res.data.map(it => {
49             let file = {};
50             if(it.contentList && it.contentList.length > 0){
51               if(zipName == ''){
52                 zipName = `${files.length > 1 ? `${files[0].name  }等` : (files.length == 1 ? files[0].name : '未知')  }.zip`
53               }
54               const firstContent = it.contentList[0];
55               file = {
56                 fileId: firstContent.fileId,
57                 fileName: firstContent.filePath,
58                 token: firstContent.token
59               }
60               if(nodeUrl == ''){
61                 nodeUrl = firstContent.nodeUrl
62               }
63             }
64             return file;
65           })
66         }
67         return {
68           fileList,
69           nodeUrl,
70           zipName
71         }
72       }
73     }).then(({fileList, nodeUrl, zipName}) => {
74       const url = `${nodeUrl  }/node/zip/json`; // 文件下載路徑
75       const form = document.createElement('form');
76       form.setAttribute("method", "post"); 
77       form.setAttribute("name", "theForm");
78       form.setAttribute("target", "_self");
79       form.setAttribute('style', 'display:none');
80       form.setAttribute("action", url); 
81       document.body.appendChild(form);
82 
83       const newinput = document.createElement('input');
84       newinput.setAttribute('type','hidden');
85       newinput.setAttribute('name','strFileList');
86       newinput.setAttribute('value',JSON.stringify(fileList))
87 
88       const newinput2 = document.createElement('input');
89       newinput2.setAttribute('type','hidden');
90       newinput2.setAttribute('name','zipName');
91       newinput2.setAttribute('value', zipName)
92 
93       form.appendChild(newinput);
94       form.appendChild(newinput2);
95       form.submit();
96       document.theForm.parentNode.removeChild(form);
97     });
98 }
View Code

 

暫時結束,以后補充,還可以采用FormData對象實現下載,和$('Form')差不多,這里就不一一列舉了

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM