1. 一般類型 平時比較常見的有 txt、png、jpg、zip、tar 等各種檔案格式,這些檔案格式中,一部分瀏覽器是會直接開啟連結顯示內容的,而另外一部分,瀏覽器不識別回應標頭,或者不能解析對應的格式,於是當做檔案直接下載下來了。如: <a href="http://barretlee.com/test.rar">file</a>這句代碼,若直接點開連結,瀏覽器將會直接下載該檔案。 2. dataURL類型 dataURL 也是十分常見的類型,他可以作為 src 或者 url() 的參數送進去。比較常見的有如下幾種: 文本: data:text/plain;這裡是本文內容。圖片: data:image/jpg;base64,/9j/4AAQSkZJRgABAQEA.... data:image/png;base64,/9j/4AAQSkZJRgABAQEA....base64 是用的比較廣泛的一種資料格式。 Base64格式data:[][;charset=][;base64],Base64 在CSS中的使用:.demoImg{ background-image: url("data:image/jpg;base64,/9j/4QMZRXhpZgAASUkqAAgAAAAL...."); }Base64 在HTML中的使用:<img width="40" height="30" src="data:image/jpg;base64,/9j/4QMZRXhpZgAASUkqAAgAAAAL...." /> 3. Blob 流 Blob 對象表示不可變的、包含未經處理資料的類檔案對象。具體的內容可以參閱MDN文檔。 他的使用也是特別的方便,如: var aFileParts = ['<a id="a"><b id="b">hey!</b></a>'];var oMyBlob = new Blob(aFileParts, {type : 'text/html'}); // the blobBlob 接收兩個參數,一個是數群組類型的資料對象,他可以是 ArrayBuffer、ArrayBufferView、Blob、String 等諸多類型;第二個參數是 MINE 類型設定。而本文我們要用到的是 URLcreateObjectURL() 這個函數,他的作用是將一個 URL 所代表的內容轉化成一個 DOMString,產生的結果是一個 檔案對象 或者 Blob 對象。 4. 二進位流 我們利用 File API 讀取檔案的時候,拿到的是資料的二進位流格式,這些類型可以直接被 ArrayBuffer 等接收,本文中沒有用到,就不細說了。 二、JavaScript 多檔案下載 HTML5 中 a 標籤多了一個屬性——download,使用者點選連結瀏覽器會開啟並顯示該連結的內容,若在連結中加了 download 屬性,點擊該連結不會開啟這個檔案,而是直接下載。雖說是比較好用,但低版本瀏覽器不相容,這個在本節的 2 和 3 中將會講到解決方案。 在這裡,我們可以利用屬性檢測UA來判斷瀏覽器類型: h5Down = document.createElement("a").hasOwnProperty("download");var h5Down = !/Trident|MSIE/.test(navigator.userAgent); // Trodent 標識 IE111. a 標籤 download 屬性的使用 註:FF5.0 / Safari5.0 / Opera11.1 / IE9.0 不支援 download 屬性 利用 download 屬性可以直接下載單個檔案,若想點擊一次下載多個檔案,就得稍加處理下了: function downloadFile(fileName, content){ var aLink = document.createElement("a"), evt = document.createEvent("HTMLEvents"); evt.initEvent("click"); aLink.download = fileName; aLink.href = content; aLink.dispatchEvent(evt);}download 屬性的作用除了讓瀏覽器忽略檔案的 MIME 類型之外,還會把該屬性的值作為檔案名稱。你可以在 chrome 控制台運行這句程式: downloadFile("barretlee.html", "./");瀏覽器會提示是否保留(下載)該 html 檔案。之前我們提到檔案類型還可能是 dataURL 或者是 Blob 流,為了讓程式也支援這些資料類型,稍微修改下上面的函數: function downloadFile(fileName, content){ var aLink = document.createElement('a'); , blob = new Blob([content]) , evt = document.createEvent("HTMLEvents"); evt.initEvent("click"); aLink.download = fileName; aLink.href = URL.createObjectURL(blob); aLink.dispatchEvent(evt);}new Blob([content]),現將檔案轉換成一個 Blog 流,然後,使用 URL.createObjectURL() 將其轉換成一個 DOMString。這樣我們就支援 data64 和其他資料類型的 content 了~ 2. window.open 之後 execCommand("SaveAs") 上面也提到了,儘管 download 屬性是十分便利的 H5 利器,但低版本 IE 根本不賞臉,要說方法,IE 還是有很多方式去轉換的,比如 ADOBE.STREAM 的 activeX 對象可以把檔案轉換成檔案流,然後寫入到一個要儲存的檔案中。這裡要談到的是略微方便一點的方式:先把內容寫到一個新開的 window 對象中,然後利用 execCommand 執行儲存命令,就相當於我們在頁面上按下 Ctrl+S,這樣頁面內的資訊都會 down 下來。 // 將檔案在一個 window 視窗中開啟,並隱藏這個視窗。var win = window.open("path/to/file.ext", "new Window", "width=0,height=0");// 在 win 視窗中按下 ctrl+s 儲存視窗內容win.document.execCommand("SaveAs", true, "filename.ext");// 使用完了,關閉視窗win.close();這個過程十分明了,不過這裡會存在一個問題,並不是程式的問題,而是瀏覽器的問題,如果我們用 搜狗瀏覽器 或者 360瀏覽器 開啟新視窗的話,他會新開一個標籤頁,而不是新開一個視窗,更可惡的時候部分瀏覽器不允許 window.open (這個可以設定)。所以只好另覓他法了。 3. iframe 中操作 既然新開一個視窗那麼麻煩,我就在本視窗下完成工作~ function IEdownloadFile(fileName, contentOrPath){ var ifr = document.createElement('iframe'); ifr.style.display = 'none'; ifr.src = contentOrPath; document.body.appendChild(ifr); // 儲存頁面 -> 儲存檔案 ifr.contentWindow.document.execCommand('SaveAs', false, fileName); document.body.removeChild(ifr);}一般的連結我們可以直接給 iframe 添加 src 屬性,然後執行 saveAs 命令,倘若我們使用的是 data64 編碼的檔案,這個怎麼辦? var isImg = contentOrPath.slice(0, 10) === "data:image"; // dataURL 的情況isImg && ifr.contentWindow.document.write("<img src='" + contentOrPath + "' />");這個也比較好處理,直接把檔案寫入到 iframe 中,然後在執行儲存。 三、代碼的封裝與介面介紹 1. 代碼的封裝以及相關 DEMO 封裝:lib.js DEMO:javascript-multiple-download (HTTPS,第三個有bug) javascript-multiple-download (HTTP,測試正常) Bug 說明,經過一番細節處理之後,基本相容各個瀏覽器,我把代碼放在 https://raw.github.com 上託管,可能因為是 https 傳輸,第三個測試中報錯了,報錯的具體內容是:HTTPS 安全受到 http://rawgithub.com/barretlee/javascript-multiple-download/master/file/test.jpg 的威脅,而 test.txt 檔案沒有報錯。放到 http 協議下測試回合結果是可觀的。(這點我沒有去深究,肯定是有深層安全方面原因的,難道就因為他是 jpg圖片格式?)後面的 demo 我放在 BAE 上,沒有問題,不過沒測試 safari 和 opera。 2. 介面的調用 提供了三個介面,支援單檔案下載,多檔案下載,多檔案下載自訂命名。 1)單檔案下載 Downer("./file/test.txt");2)多檔案下載 Downer(["./file/test.txt","./file/test.txt"]);3)多檔案下載自訂命名 Downer({ "1.txt":"./file/test.txt", "2.jpg":"./file/test.jpg"}); 檔案的 URL 如 ./file/test.txt 都可以改成 base64 或者其他格式。 四、伺服器支援與後端實現 1. 後端實現 後端實現的原理,就是在回應標頭中加入一些特殊的標記,如前端發送這樣的請求: function download(path) { var ifrm = document.getElementById(frame); ifrm.src = "download.php?path="+path;}後端的響應為 <?php header("Content-Type: application/octet-stream"); header("Content-Disposition: attachment; filename=".$_GET['path']); readfile($_GET['path']);?>告訴瀏覽器這是一個流檔案,作為附件方式發送給你,請忽略 MINE type,直接儲存。 2. 伺服器配置 若後台是 apche 作為伺服器,可以配置 htaccess 檔案: <filesmatch "\.(zip|rar)$"="">Header set Content-Disposition attachment</filesmatch>意思是只要請求的是 zip 或者 rar 類型的檔案,那麼久添加一個 Content-Disposition:attachment 的回應標頭。這樣就可以在 php 代碼中省略麻煩的操作。