前端學習隨記の下載上傳篇 —— 下載篇
閑言: 懶懶懶?。?!好長時間不寫文章了,一直出差魔都,學習一些其他知識開發(fā)項目,也時不時的空閑時間深度學習一些前端基礎知識。好吧,不找理由,就是懶哇哈哈(????)。
參考傳送門:
http://www.cnblogs.com/voiphudong/p/3284724.html
https://scarletsky.github.io/2016/07/03/download-file-using-javascript/#參考資料
https://developer.mozilla.org/zh-CN/docs/Web/API/FileSystem
引言###
上傳下載一直是項目上非常常見的需求,也是對于我這種前端小白來說一直隱隱犯怵的地方,不太熟悉,加上項目上用過一次,同事朋友也問過遇到過,所以打算好好惡補這塊↖(ω)↗。本篇先說下下載,上傳留著下篇來說,歡迎關注留bug.
幾種下載方式
前后端下載方式原理簡述:
1)標準URL下載方式:?可以通過在web頁面中嵌入 url超級鏈接,標準的HTTP GET請求。對于服務器端web根目錄有一個test.zip的文件。此種方法的弊端是完全暴露了文件test.zip的網(wǎng)站路徑,而且動態(tài)性不夠靈活。
2)通過服務器端腳本向瀏覽器方(stdout)輸出二進制流的方式下載:?上述方法可以在服務器端 通過 腳本程序 download.php ,并且根據(jù)傳入的查詢字符串f=test.zip定位服務器上文件系統(tǒng)test.zip的路徑,然后按二進制流的方式發(fā)送給客戶端瀏覽器,那么客戶端瀏覽器就會彈出一個“下載對話框”了。
form 表單下載
POST的URL下載鏈接,可以通過設置form表單的action 以及 表單元素值來完成下載。
function downLoad () {
var form = document.createElement('form'); //定義一個form表單
form.style = 'display:none'; //下面為在form表單中添加查詢參數(shù)
form.target = '';
form.method = 'post';
form.action = url;//下載接口
var input1 = document.createElement('input');
input1.type = 'hidden';
Input1.name = 'exportData';
input1.value = (new Date()).getMilliseconds();
document.body.appendChild(form) //將表單放置在web中
form.appendChild(input1); //將查詢參數(shù)控件提交到表單上
form.submit(); //表單提交
}
iframe
iframe有一個src屬性,其本質就是發(fā)送http請求,GET一個頁面或者數(shù)據(jù),可以通過一個動態(tài)生成的隱藏的iframe來得到下載的二進制文件。
function download(){
var IFrameRequest=document.createElement("iframe");
IFrameRequest.id="IFrameRequest";
IFrameRequest.src="/test.zip";
IFrameRequest.style.display="none";
document.body.appendChild(IFrameRequest);
}
HTML5のdownload屬性
由于瀏覽器的安全策略的限制,我們通常只能通過一個額外的頁面,訪問某個文件的 url 來實現(xiàn)下載功能,但是這種用戶體驗非常不好。HTML 5 里面為 a 標簽添加了一個 download 的屬性,我們可以輕易的利用它來實現(xiàn)下載功能。
<a href="http://somehost/somefile.zip" download="filename.zip">Download file</a>
a標簽添加 download 屬性,我們點擊這個鏈接的時候就會自動下載文件了~
順便說下,download 的屬性值是可選的,它用來指定下載文件的文件名。像上面的例子中,我們下載到本地的文件名就會是 filename.zip 拉,如果不指定的話,它就會是 somefile.zip 這個名字啦!
當然,我們還可以動態(tài)創(chuàng)建a標簽實現(xiàn)下載:
function download() {
var a = document.createElement('a');
var url = window.URL.createObjectURL(blob);
var filename = 'what-you-want.txt';
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);
}
window.URL 里面有兩個方法:
createObjectURL 用 blob 對象來創(chuàng)建一個 object URL(它是一個 DOMString),我們可以用這個 object URL 來表示某個 blob 對象,這個 object URL 可以用在 href 和 src 之類的屬性上。
revokeObjectURL 釋放由 createObjectURL 創(chuàng)建的 object URL,當該 object URL 不需要的時候,我們要主動調用這個方法來獲取最佳性能和內存使用。
當然,如果你沒有blob對象的url,也可以不用window.URL,直接使用下載地址url。
HTML5のFileSystem
由于這種方法目前大多數(shù)瀏覽器不支持,僅僅chrome支持,具體方式可以查看大神們的文章,我就不搬運了。(????)
https://imququ.com/post/a-downloader-with-filesystem-api.html
其他方式
Window.open(url):這種方式在目前的網(wǎng)絡環(huán)境及安全限制下被攔截已經(jīng)不太好使了,主要用來是打開新窗口下載。
var new_window = null;
function downLoad () {
var isSure=confirm("是否下載")
if (isSure){
new_window = window.open()
new_window.location.;
document.getElementById('result').innerHTML = "You downLoad OK!"
}
else{
document.getElementById('result').innerHTML = "You pressed Cancel!"
}
}
function closeDownLoad() {
new_window && new_window.close();
}
//使用a鏈接target='_blank',則不會攔截下載
function AdownLoad() {
var url = 'https://github.com/WZOnePiece/study-draggable/archive/master.zip';
var a = document.createElement("a");
a.setAttribute("href", url);
a.setAttribute("target", "_blank");
a.setAttribute("id", "camnpr");
document.body.appendChild(a);
a.click();
}
這里主要是用window.open 打開窗口,接著使用location.href = url進行重定向避免攔截進行下載。
補充知識
Blob 對象
Blob 全稱是 Binary large object,它表示一個類文件對象,可以用它來表示一個文件。根據(jù) MDN 上面的說法,F(xiàn)ile API 也是基于 blob 來實現(xiàn)的。
構建 blob 的方式就是通過服務器返回的文件來創(chuàng)建 blob ,采用File API及Fetch API。
fetch('http://somehost/somefile.zip').then(res => res.blob().then(blob => {
var a = document.createElement('a');
var url = window.URL.createObjectURL(blob);
var filename = 'myfile.zip';
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);
}))
注意:Fetch API 目前瀏覽器支持不好,詳情看https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch
File API的詳細介紹:https://developer.mozilla.org/zh-CN/docs/Using_files_from_web_applications
可以利用fetch配合reader對象來實現(xiàn)進度條功能
限制
1、不同瀏覽器對 blob 對象有不同的限制
Supported browsers
| Browser | Constructs as | Filenames | Max Blob Size | Dependencies |
|---|---|---|---|---|
| Firefox 20+ | Blob | Yes | 800 MiB | None |
| Firefox < 20 | data: URI | No | n/a | Blob.js |
| Chrome | Blob | Yes | [500 MiB][3] | None |
| Chrome for Android | Blob | Yes | [500 MiB][3] | None |
| Edge | Blob | Yes | ? | None |
| IE 10+ | Blob | Yes | 600 MiB | None |
| Opera 15+ | Blob | Yes | 500 MiB | None |
| Opera < 15 | data: URI | No | n/a | Blob.js |
| Safari 6.1+* | Blob | No | ? | None |
| Safari < 6 | data: URI | No | n/a | Blob.js |
2、構建完 blob 對象后才會轉換成文件。
文件大點的話,要等到下載完文件之后才能構建 blob 對象,再轉化成文件,頁面顯示上不太友好,沒有提示。這時建議采用a標簽,防止用戶的錯誤認知造成重復點擊下載,資源浪費。
服務器出錯
- 1、window.open(url)
打開一個帶錯誤信息的頁面
- 2、window.location.href
頁面將跳轉到一個錯誤頁面
- 3、iframe
用戶感知不到任何變化
- 4、a標簽
直接出現(xiàn) 下載失敗
當然,如果response header里有content-disposition字段的話,瀏覽器都會下載一個帶錯誤信息的文件。這時候,其實我們可以多發(fā)一個ajax/fetch請求,先檢測下接口狀態(tài),然后再取做下載邏輯。但這樣就對服務器造成了額外的開銷。
這樣的體驗都不太好,作為一個追求極致體驗的程序猿,我們應該重新思考下,如何提升用戶體驗。
結語##
O(∩_∩)O,這篇下載篇就這樣簡單介紹下,至于還有其他的方式,歡迎留言告知;代碼及描述上有bug也希望不吝指出。謝謝哈!