前端很多項目中,都有文件下載的需求,特別是JS生成文件內容,然后讓瀏覽器執行下載操作(例如在線圖片編輯、在線代碼編輯、iPresst等)。但受限于瀏覽器,很多情況下我們都只能給出個鏈接,讓用戶點擊打開-》另存為。如下面這個鏈接: <a href=”file.js”>file.js</a> 用戶點擊這個鏈接的時候,瀏覽器會打開并顯示鏈接指向的文件內容,顯然,這并沒有實現我們的需求。HTML5中給a標簽增加了一個download屬性,只要有這個屬性,點擊這個鏈接時瀏覽器就不在打開鏈接指向的文件,而是改為下載(目前只有chrome、firefox和opera支持)。下載時會直接使用鏈接的名字來作為文件名,但是是可以改的,只要給download加上想要的文件名即可,如:download=“not-a-file.js”。Not enough!但是這樣還不夠,以上的方法只適合用在文件是在服務器上的情況。如果在瀏覽器端js生成的內容,想讓瀏覽器進行下載要如何辦到呢?其實還是有辦法辦到的,相信很多人都多少聽過了DataURI這個詞,比較常見的就是圖片的src,如: <img src=”data:image/gif;base64,R0lGOXXXXX"> DataURI的解釋可以移步這里,本人就不在解釋了。那么,現在要將js生成的內容進行下載就有法可依了。封裝成一個方法如下:JavaScript function downloadFile(aLink, fileName, content){aLink.download = fileName;aLink.href = "data:text/plain," + content; } 調用downloadFile之后,用戶點擊鏈接,就能觸發瀏覽器下載。Not enough!但是,還不夠,上面的辦法有兩個硬傷,會導致流失很多懶人美眉:下載的文件類型限制死了,美眉要下載處理后的果照怎么辦?下載還要再點擊一下,太麻煩啦。要解決文件類型的問題,可以用瀏覽器的新API(URL.createObjectURL)來解決問題,URL.createObjectURL通常都是用來創建圖片的DataURI用來顯示圖片,這里用來下載文件,讓瀏覽器來幫我們設定好文件類型。URL.createObjectURL的參數是File對象或者Blob對象,File對象也就是通過input[type=file]選擇的文件,Blob對象是二進制大對象,詳細說明可參考這里。現在,我們只要用content創建一個ObjectURL并賦值給aLink即可解決文件類型的限制問題。文件的自動下載也挺好辦,自己構建一個UI點擊事件,再自動觸發下,就能實現自動下載啦。現在來看看最終代碼:JavaScript/*let url = new URL('https://example.com?foo=1&bar=2');console.log(url);URL {href:"https://example.com/?foo=1&bar=2", origin: "https://example.com", protocol: "https:", username: "", password: "", …}hash:""host:"example.com"hostname:"example.com"href:"https://example.com/?foo=1&bar=2"origin:"https://example.com"password:""pathname:"/"port:""protocol:"https:"search:"?foo=1&bar=2"searchParams:URLSearchParams {}username:""}/*arrayObject.slice(start,end)返回一個新的數組,包含從start到end(不包括該元素)的 arrayObject 中的元素。 */ /*去掉?,剩下let params = new URLSearchParams(url.search.slice(1));//添加第二個foo搜索參數。params.append('foo', 4);//查詢字符串變成: 'foo=1&bar=2&foo=4' *///下載功能實現:用iframes,其實就是通過iframes給后臺發請求,讓后臺實現具體下載 import axios from 'axios'; export const baseURL = '/service'; export function getFileName(headers) {//headers['content-disposition']的屬性值中的'attachment; filename='用空格代替,拿到file的值return headers['content-disposition'].replace('attachment; filename=', ''); } export function downloadData(url,params) {//URLSearchParams 接口定義了一些實用的方法來處理 URL 的查詢字符串var searchParams = new URLSearchParams();url = baseURL + url;for (let key of Object.keys(params)) {searchParams.append(key, encodeURIComponent(params[key]));}document.querySelector('#downloadIframe').setAttribute('src', url + '?' + searchParams) }//下載功能的實現:用a.download, 后臺返回的 res.data 必須是 blob 對象 export function download(res) {var a = document.createElement('a');//URL.createObjectURL通常都是用來創建圖片的DataURI用來顯示圖片,這里用來下載文件,讓瀏覽器來幫我們設定好文件類型var url = window.URL.createObjectURL(res.data);var filename = getFileName(res.headers);a.href = url;//HTML5中給a標簽增加了一個download屬性,只要有這個屬性,點擊這個鏈接時瀏覽器就不在打開鏈接指向的文件,而是改為下載a.download = filename;a.click();//URL.revokeObjectURL()靜態方法用來釋放一個之前通過調用 URL.createObjectURL() 創建的已經存在的 URL 對象。//當你結束使用某個 URL 對象時,應該通過調用這個方法來讓瀏覽器知道不再需要保持這個文件的引用了。window.URL.revokeObjectURL(url); } function filterResponse(res) {if (isDownload(res.headers)) {download(res);} } function errorResponse(err) {return Promise.reject(err); } httpLayer.interceptors.request.use(filterResponse, errorResponse);header中Content-Disposition的作用與使用方法:Content-disposition 是 MIME 協議的擴展,MIME 協議指示 MIME 用戶代理如何顯示附加的文件。Content-disposition其實可以控制用戶請求所得的內容存為一個文件的時候提供一個默認的文件名,文件直接在瀏覽器上顯示或者在訪問時彈出文件下載對話框。 格式說明: content-disposition = "Content-Disposition" ":" disposition-type *( ";" disposition-parm ) 字段說明: Content-Disposition為屬性名 disposition-type是以什么方式下載,如attachment為以附件方式下載 disposition-parm為默認保存時的文件名 服務端向客戶端游覽器發送文件時,如果是瀏覽器支持的文件類型,一般會默認使用瀏覽器打開,比如txt、jpg等,會直接在瀏覽器中顯示,如果需要提示用戶保存,就要利用Content-Disposition進行一下處理,關鍵在于一定要加上attachment: 復制代碼 代碼如下: Response.AppendHeader("Content-Disposition","attachment;filename=FileName.txt"); 備注:這樣瀏覽器會提示保存還是打開,即使選擇打開,也會使用相關聯的程序比如記事本打開,而不是IE直接打開了。 Content-Disposition就是當用戶想把請求所得的內容存為一個文件的時候提供一個默認的文件名。具體的定義如下: 復制代碼 代碼如下: content-disposition = "Content-Disposition" ":"disposition-type *( ";" disposition-parm ) disposition-type = "attachment" | disp-extension-token disposition-parm = filename-parm | disp-extension-parm filename-parm = "filename" "=" quoted-string disp-extension-token = token disp-extension-parm = token "=" ( token | quoted-string ) 那么由上可知具體的例子: Content-Disposition: attachment; filename="filename.xls" 當然filename參數可以包含路徑信息,但User-Agnet會忽略掉這些信息,只會把路徑信息的最后一部分做為文件名。 當你在響應類型為application/octet- stream情況下使用了這個頭信息的話,那就意味著你不想直接顯示內容, 而是彈出一個"文件下載"的對話框,接下來就是由你來決定"打開"還是"保存" 了。 注意事項: 1.當代碼里面使用Content-Disposition來確保瀏覽器彈出下載對話框的時候。 response.addHeader("Content-Disposition","attachment");一定要確保沒有做過關于禁止瀏覽器緩存的操作。如下: 復制代碼 代碼如下:response.setHeader("Pragma", "No-cache"); response.setHeader("Cache-Control", "No-cache"); response.setDateHeader("Expires", 0);