H5拖放非同步檔案上傳之二——上傳進度監聽
上一篇,我們走通了拖放檔案上傳的整個流程,但離實際使用場景還有差距。這篇,我們來新增幾個實際使用場景必要的功能,向實際使用再走一步。
新增功能
顯示待上傳檔案列表;
支援移除待上傳檔案;
使用upload.onprogress顯示上傳進度;
支援中斷上傳;
upload.progress
XMLHttpRequest.upload方法返回一個 XMLHttpRequestUpload物件,用來表示上傳的進度。這個物件是不透明的,但是作為一個XMLHttpRequestEventTarget,可以透過對其繫結事件來追蹤它的進度。onprogres監聽資料傳輸進行中(透過監聽這個事件,可獲得上傳進度)。
實現思路
拖放檔案到上傳區域時,將檔案儲存在一個檔案陣列中,新增並顯示檔案相關資訊和移除按鈕,點選移除按鈕刪除檔案陣列中對應的檔案元素,點選上傳按鈕,遍歷檔案陣列,開始上傳待上傳檔案,此時點選移除按鈕則中止檔案上傳。
程式碼
//相關樣式.drop-area{ margin:auto; width: 500px; height: 500px; border:1px pink dashed; } .close-btn{ cursor: pointer; } .close-btn:after{ float: right; content: 'X'; color: red; }#fileList{ width: 95%; } .process-bar{ position: relative; margin: 0 10px 0 10px; width: 198px; height: 18px; display: none; text-align: center; line-height: 20px; color: #6dbfff; border-radius: 3px; border: 1px solid #1483d8; background: #fff;} .success .process-bar, .uploading .process-bar{ display: inline-block; } .process-bar .process-text{ position: relative; z-index: 1; } .process-bar .process-rate{ position: absolute; width: 0; height: 100%; left: 0; top: 0; border-radius: 3px; background: #1483d8;} .file-list .success .process-text, .file-list .success .close-btn:after, .file-list .error .process-text, .file-list .error .close-btn:after{ display: none; } .success .process-bar :after, .error .process-bar :after{ content:'success'; position: absolute; margin: auto; left: 0; right: 0; z-index: 2; } .error .process-bar:after{ content: "error"; color: red; }
<!--HTML--><!--監聽拖放DOM--><!--顯示待上傳檔案列表--><!--上傳按鈕-->
//Javascript程式碼let filesSet = []; //檔案儲存陣列let fileList = document.getElementById('fileList'); //獲取顯示檔案列表DOM/** * 建立一個新的空白的文件片段frag * 用於附加多個待上傳檔案的DOM,可減小回流 * https://developer.mozilla.org/zh-CN/docs/Web/API/Document/createDocumentFragment */let frag = document.createDocumentFragment();let barDom = createProccessBar(); //建立進度條DOMlet submit = document.getElementById("submit") //獲取提交按鈕/* 拖動到放置區域時 */function dragover_hander (event) { /* 必須同時阻止dragover和drop的預設事件 否則會響應瀏覽器預設行為 瀏覽器器能顯示的檔案會直接顯示,例如html檔案、圖片檔案 瀏覽器器不能顯示的檔案會出現檔案下載彈窗 */ event.preventDefault(); }/*拖放完成事件*/function drop_hander (event) { event.preventDefault(); //阻止預設事件 var files = event.dataTransfer.files; //透過dataTransfer物件獲取檔案物件陣列 for(let file of files) { //遍歷檔案物件陣列 //建立檔案資訊顯示DOM,並儲存在file物件的element屬性中,用於後續操作 file.element = createFileDom(file, filesSet.length) //複製進度條DOM,並儲存在file物件的processBar屬性中,用於後續操作 file.processBar = filesSet.length?barDom.cloneNode(true):barDom; //將進度條新增至檔案資訊顯示DOM中 file.element.appendChild(file.processBar); //file檔案物件新增到檔案儲存陣列 filesSet.push(file); //建立檔案資訊顯示DOM新增至文件片段frag frag.appendChild(file.element); } //將檔案列表新增至顯示檔案列表的div fileList.appendChild(frag) }/** * 建立檔案資訊顯示DOM * file 檔案物件,用於獲取檔案資訊 * index 檔案物件在陣列中的索引,用於刪除 */function createFileDom (file, index) { let p = document.createElement('p'); //file.name屬性可以獲得檔名稱 //有興趣的童鞋,可以使用for...in迴圈檢視一下file物件的其它屬性值 let text = document.createTextNode(file.name); let span = document.createElement("span"); span.setAttribute('index', index); //索引新增至按鈕的index屬性 span.className = 'close-btn'; p.appendChild(text); p.appendChild(span); return p; //返回檔案資訊顯示DOM}/** * 建立進度條DOM, * 進度條DOM結構固定,可使用clone(true)進行復制 * @return {[type]} [description] */function createProccessBar() { let bar = document.createElement("span"); let rate = document.createElement("span"); let text = document.createElement("span"); bar.className = "process-bar"; rate.className = "process-rate"; text.className = "process-text"; text.innerText="0%"; bar.appendChild(text); bar.appendChild(rate); return bar; } //透過事件代理,監聽移除或中止上傳fileList.addEventListener('click', (evt)=>{ let index = evt.target.getAttribute('index'); //獲得index屬性值 if (index) { //存在index屬性值,表示點選了刪除按鈕 if (filesSet[index].unloading && filesSet[index].req) { //檔案已經上傳中 filesSet[index].req.abort(); //中止上傳 filesSet[index].unloading = false; //將上傳中的狀態設定為false } else { //未開始上傳 filesSet[index].element.remove(); //移除DOM filesSet[index].element = null; //釋放對DOM的引用 filesSet[index].processBar = null;//釋放對DOM的引用 delete filesSet[index];//刪除檔案陣列中對應的元素 } } }) submit.addEventListener('click',function(){// 為上傳按鈕繫結事件 //這裡使用for...in迴圈,正好可以避免對陣列稀疏元素的遍歷 for(let key in filesSet){ //如果正在上傳中或已經上傳完成,不再上傳 if (filesSet[key].unloading || filesSet[key].uploaded) continue; filesSet[key].unloading = true; //標記開始上傳 //建立一個檔案上傳的Promise,並設定成功及失敗的回撥 initUpload(filesSet[key]).then(file => { //上傳成功 file.element.className = "success"; //UI顯示成功資訊 file.uploaded = true; //標記上傳成功 }).catch((file, err) => { file.element.className = "error"; //UI顯示失敗資訊 //取消開始上傳標記,點選上傳按鈕將嘗試再次上傳 filesSet[key].unloading = false; }) } })/** * 返回一個檔案上傳的Promise例項 * @param {[type]} file 要上傳的檔案 */function initUpload(file){ return new Promise((res, rej) => { let formData = new FormData();//宣告一個FormData例項 let req = new XMLHttpRequest();//建立XHR例項 let reta = file.processBar.querySelector('.process-rate');//獲得進度條DOM let text = file.processBar.querySelector('.process-text');//獲得進度文字DOM let pre;//儲存上傳百分比 //監聽資料傳輸進行中 req.upload.onprogress =function(data){ pre = (data.loaded/data.total*100).toFixed(2);//計算百分比 reta.style.width = pre +'%';//修改進度條 text.innerText = pre +'%' ;//修改進度條文字 } //監聽請求完成 req.onreadystatechange = function () { if (req.readyState !==4 ) return ; if (req.status === 200 ){ //完成,執行成功回撥 res(file, req) } else { //失敗,執行失敗回撥 rej(file, req) } } formData.append('file',file); //使用append方法新增檔案到file鍵 req.open('POST', '/process_post'); //初始化請求 req.send(formData); //傳送請求 file.req = req; //儲存req物件,用於中止請求 //形如顯示上傳進度 file.element.className = "uploading" }) }
到這裡程式碼就結束了,完整程式碼可以檢視。因為是本地上傳,在測試的時候可以使用大一些的檔案,或者限制一下上傳。
結束語
這些新的API,使得檔案拖放上傳變得簡單起來。可惜低版本的IE並不支援,聽說低版本的IE可以使用Falsh來進行檔案上傳,具體是如何實現的,要不我們下篇再來探討一下。
作者:快減肥成功的胖紙
連結:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3407/viewspace-2812770/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 檔案上傳進度提示
- Blazor 使用拖放(drag and drop)上傳檔案Blazor
- js實現帶上傳進度的檔案上傳JS
- vue中檔案上傳阿里雲並獲取上傳進度Vue阿里
- Android 史上最優雅的實現檔案上傳、下載及進度的監聽Android
- Android OkHttp+RxJava 史上最優雅的實現檔案上傳/下載進度的監聽AndroidHTTPRxJava
- Java大檔案上傳、分片上傳、多檔案上傳、斷點續傳、上傳檔案minio、分片上傳minio等解決方案Java斷點
- 單個檔案上傳和批量檔案上傳
- 前端大檔案上傳/分片上傳前端
- 檔案上傳
- Simple WPF: S3實現MINIO大檔案上傳並顯示上傳進度S3
- 09-XSS鍵盤監聽、cookie竊取&檔案上傳繞過Cookie
- 檔案上傳之三基於flash的檔案上傳
- node使用multer進行檔案上傳
- PHP上傳檔案PHP
- 檔案上傳概述
- ajaxfileupload 檔案上傳
- Flask——檔案上傳Flask
- Linux上傳檔案Linux
- 檔案上傳漏洞
- minio上傳檔案
- SpringBoot上傳檔案Spring Boot
- JavaScript 檔案上傳JavaScript
- Git上傳檔案Git
- vue-resource+iview上傳檔案取消上傳VueView
- .NET Core 如何上傳檔案及處理大檔案上傳
- Linux伺服器上傳檔案傳送檔案Linux伺服器
- PHP ftp上傳檔案PHPFTP
- 上傳檔案專題
- 上傳檔案至GitHubGithub
- 使用fileinput上傳檔案
- WebAPI Angularjs 上傳檔案WebAPIAngularJS
- Ajax 之檔案上傳
- 檔案上傳測試
- HTTP檔案上傳原理HTTP
- SpringMVC之檔案上傳SpringMVC
- 上傳檔案的陷阱
- laravel 多檔案上傳Laravel