還記得第一次做文檔下載的時候,基於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 }
暫時結束,以后補充,還可以采用FormData對象實現下載,和$('Form')差不多,這里就不一一列舉了