業務中要求的是示例放在靜態文件夾中,並不須要請求後臺。針對這種場景,筆者將介紹三種方法,分別是 window.open,form表單提交以及a標籤 下載。筆者將經過下載img和word文檔的例子,對這三種方法進行對比。html
現構建dom結構以下:ios
<button onClick={this.windowOpen}>window.open</button> <button onClick={this.formSubmit}>formSubmit</button> <button onClick={this.aDownload}>aDownload</button>
window.open
:import gakkiURL from './gakki.jpg'; import wordURL from './wordURL.doc'; windowOpen = () => { window.open(gakkiURL); //window.open(wordURL); }
該方法在請求兩種文件時,具體表現爲:web
img:新開網頁,而後顯示對應的img圖片。ajax
word:下載該文件。axios
formSubmit = () => { let form = document.createElement('form'); form.method = 'get'; form.action = gakkiURL; //form.action = wordURL; //form.target = '_blank'; // form新開頁面 document.body.appendChild(form); // form表單作出提交操做要先加入到dom樹中 form.submit(); document.body.removeChild(form); }
該方法在請求兩種文件時,具體表現爲:數組
img:form在不設置target時,會在當前頁面打開url,顯示圖片。promise
word:下載該文件。瀏覽器
從上述兩種方法能夠看出,在請求對應的url時, 瀏覽器針對不一樣的MIME類型會選擇不一樣的處理方式 。在請求img、txt等格式時,瀏覽器會打開對應的文件,而不是下載。若是想要img這些格式也下載呢?此時就須要方法三。app
// 使用a標籤 aDownload = () => { const a = document.createElement('a'); a.href = gakkiURL; //a.href = wordURL; //a.download = 'gakki.jpg'; a.click(); }
a標籤在不加download屬性時表現同上兩種方法,而在加了 download
屬性後,可成功觸發img等格式的下載。dom
該屬性能夠設置一個值來規定下載文件的名稱。所容許的值沒有限制,瀏覽器將自動檢測正確的文件擴展名並添加到文件 (.img, .pdf, .txt, .html, 等等)。
最終對比效果:
經常使用方法是使用 type="file"
的input標籤觸發下載,而後使用formData傳輸數據,代碼以下:
// 點擊上傳文檔 handleClick = () => { const input = document.createElement('input'); input.type = 'file'; input.accept = 'application/msword, application/vnd.openxmlformats-officedocument.wordprocessingml.document'; // word文件對應的MIME類型 input.onchange = (e) => { const file = e.target.files.items(0);// files[0]也行 console.table(file); // 檢查文檔格式 if (!this.checkDocument()) { e.target.value = ''; return; } // 上傳文檔 this.uploadDocument(file); }; input.click(); };
accept
:代表input接受的文件的MIME類型。.doc和.docx相應的MIME類型在源碼中已標明。
fileList對象可經過items或數組索引的形式得到對應的file對象。file對象經常使用的屬性有:lastModified、type、name和size。可經過這些屬性自定義檢查文檔格式。
檢查文檔的代碼checkDocument以下:
// 文檔檢查 checkDocument = file => { const accept = ['.doc', '.docx']; const index = file.name.lastIndexOf('.'); if (index < 0 || accept.indexOf(file.name.substr(index).toLowerCase()) < 0) { // 檢查文件類型 Message.error('暫不支持該文件格式'); return false; } if (file.size > 2 * 1024 * 1024) { // 檢查文件大小 Message.error('文檔大於2MB,上傳失敗'); return false; } return true; };
以後是上傳文檔uploadDocument:
// 上傳文檔 uploadDocument = file => { const index = file.name.lastIndexOf('.'); const fileName = file.name.slice(0, index); const formData = new FormData(); formData.append('file', file); // ajax、fetch或axios等方式上傳 ... };
上傳文檔後須要去獲取上傳進度顯示進度條,下面將對 ajax、fetch和axios對progress事件的支持狀況 分別予以介紹。
原生支持progress事件,可用於獲取上傳進度和下載進度,分別爲 xhr.upload.onprogress
和 xhr.onprogress
事件。代碼以下:
xhr.upload.onprogress = ev => console.log((ev.loaded / ev.total) * 100)
另外可使用 xhr.abort()
取消文件上傳。
不支持progress事件,因此沒法獲取上傳進度。可是筆者在查閱資料時發現因爲res.body是可讀字節流對象,因此可使用res.body對象支持的 getReader()
屬性得到 下載進度 ,具體文獻請參考 jakearchibald.com/2016/stream… 。此處代碼與上傳的需求無關,僅做爲fetch的相關拓展,可直接跳過這一段。
res.body.getReader()
方法用於讀取響應的原始字節流,該字節流是能夠循環讀取的,直至body內容傳輸完成;
fetch(url, options).then(res => { let reader = res.body.getReader(); let loaded = 0; // read()方法返回一個promise,接受值時resolve。 reader.read().then(function processResult(result) { // result對象有兩個屬性: // done:完成時爲true // value 數據 if (result.done) { // 完成時退出循環 console.log("Fetch complete"); return; } loaded += result.value.length;// 長度,單位:字節 console.log('Received', loaded, 'bytes of data so far'); // 循環讀取 return reader.read().then(processResult); }); });
經過 onUploadProgress
和 onDownloadProgress
實現上傳和下載。
onUploadProgress(ev) => { length = Math.round((ev.loaded / ev.total) * 100); console.log(length); }
axios使用 cancel token
取消請求
var CancelToken = axios.CancelToken; var source = CancelToken.source(); axios.get(url, { cancelToken: source.token }) source.cancel();//取消請求
ajax和axios對progress事件都進行了很好地支持,而fetch因爲缺乏對progress事件的支持在這裏沒法使用。
實現了點擊上傳、得到上傳進度以及取消上傳等功能後,接下來要完成的是實現拖拽上傳。實現前,我將對有關事件進行介紹。 首先是拖拽物體時發生的事件: onDragStart
, onDrag
和 onDragEnd
,事件與被拖拽的物體有關。
onDragStart
:拖拽開始
onDrag
:拖拽中持續觸發
onDragEnd
:拖拽結束,不管是否能夠放置均觸發事件
而後是放置文件時要觸發的事件: onDragEnter
, onDragOver
, onDragLeave
和 onDrop
,事件與要拖放進的區域有關。
onDragEnter
:拖拽的物體進入時觸發
onDragOver
:拖拽的物體在區域中拖動時持續觸發
onDragLeave
:拖拽的物體離開區域時觸發
onDrop
:拖拽的物體放置在區域中時觸發
項目中爲了可以拖拽word文檔,須要在容器上取消該容器默認的onDragEnter和onDragOver事件,這是由於:
事件的偵聽器 dragenter
或 dragover
事件被用來表示有效的 drop 目標,也就是拖放項目可能被 dropped 的地方。web頁面或應用程序的大多數區域都不是 drop 數據的有效位置。所以,這些事件的默認處理是不容許出現 drop。
若是您想要容許 drop,您必須經過取消事件來防止默認的處理。您能夠經過從attribute-defined 事件監聽器返回 false
,或者經過調用事件的 preventDefault
方法來實現這一點。後者在一個單獨的腳本中定義的函數中可能更可行。
dom結構以下:
<div styleName="dropbox" onDragOver={this.preventDefault} onDragEnter={this.preventDefault} onDrop={this.handleDrop} > <div styleName="word-img" /> {this.renderBtnByUpload(this.state.uploadStatus)} // 根據上傳狀態決定是"上傳文件"仍是"取消上傳" </div>
在將文件拖拽到內容區放置後,能夠經過 dataTransfer
對象得到file信息。最終的handleDrop事件以下:
// 拖拽上傳 handleDrop = (e) => { const file = e.dataTransfer.files[0]; if (e.dataTransfer.files.length > 1) { Message.error('僅支持上傳一個word文件'); return; } if (!this.checkDocument(file)) { // 上傳失敗直接退出 e.target.value = ''; return; } this.uploadDocument(file); // 上傳文件 }
最終,實現的整體思路就是,首先構建放置文件的容器,而後給該容器取消默認的 onDragOver
和 onDragEnter
事件,當拖拽文件到容器中時經過 dataTransfer.files
拿到文件並上傳,使用ajax或axios等方式提供的progress事件拿到長度,將該長度傳到progressBar組件中,最後展現出來。