vue+element-ui+xlsx實現校驗前端上傳的Excel文件
- 2019 年 10 月 5 日
- 筆記
運行程式碼請看:https://github.com/GloryXu/vue
背景
項目中需要校驗上傳的Excel模板是否符合規範。一開始的想法是在後端進行校驗,但是後來想到一個跑批的文件最大是2M,如果放置在後端校驗,對於不規範的文件,這2M的傳輸也就白費了,同時,對於用戶的體驗也很不好,就想把校驗放置在客戶端,不合乎規範的文件直接拒掉,節省頻寬同時客戶體驗也有所改善。
程式碼歷程
項目是使用vue+element-ui構建的web項目,那麼要實現前端Excel解析,就需要添加xlsx.js插件。 安裝插件npm i xlsx --save,當然也可以這樣cnpm i xlsx --save。
失敗的程式碼
剛開始的使用element-ui的程式碼如下,實現的上傳文件功能。本例中以下Excel格式則會校驗通過。
<el-upload style="display: inline; margin-left: 10px;margin-right: 10px;" action="#" ref="fileupload" :show-file-list="false" :http-request="upLoadChange" :before-upload="beforeUpload"> <el-button size="small" type="primary">上傳文件<i class="el-icon-upload el-icon--right"></i></el-button> </el-upload>
將校驗的程式碼放置在beforeUpload方法中,程式碼如下:
beforeUpload(file) { // 讀取Excel文件並校驗返回Boolean值 let readExcelResult = this.readExcel(file); console.log(readExcelResult); if (readExcelResult) { const isLt2M = file.size / 1024 / 1024 < 2; if (!isLt2M) { this.$message.error('文件大小不能超過2MB!'); return false; } this.$message.success('校驗成功!'); return true; } else { this.$message.error('校驗文件失敗'); return false; } }readExcel方法程式碼如下:
readExcel(file) {// 解析Excel let _this = this; const reader = new FileReader(); reader.onload = (e) => { try { // 以二進位流方式讀取得到整份excel表格對象 var data = e.target.result, workbook = XLSX.read(data, {type: 'binary'}); } catch (e) { this.$message.error(e.message); return false; } // 表格的表格範圍,可用於判斷表頭是否數量是否正確 var fromTo = ''; // 遍歷每張表讀取 for (var sheet in workbook.Sheets) { let sheetInfos = workbook.Sheets[sheet]; let locations = [];// A1,B1,C1... if (workbook.Sheets.hasOwnProperty(sheet)) { fromTo = sheetInfos['!ref'];// A1:B5 // 該方法獲取每列第一行的值,如:輸入A1:B5,返回['A1','B1'],該方法詳情可查看文章末尾的github具體程式碼 locations = _this.getLocationsKeys(fromTo); } for (let i = 0;i < locations.length; i++) { let value = sheetInfos[locations[i]].v; if (value != i) { this.$message.error(locations[i] + ''s parameter isn't ' + i); return false; } } return true; } }; reader.readAsBinaryString(file); }發現問題
程式碼看上去沒問題,但是一直執行不通過,後來F12跟蹤了下,發現readExcelResult一直是undefined,所以就導致校驗異常。
優化的程式碼
- 相信絕大部分人都發現了問題,在
readExcel方法中load相關的程式碼是非同步執行的,並不會阻塞,所以此方法很快就執行結束了,結果什麼也沒返回,就直接導致了上面的問題,readExcelResult為undefined,此時就開始搜非同步轉同步,果不其然,結果都是使用promise來實現。 - 再看到
element-ui的官方文檔看到了這麼一句話: 看來只能是它了,來實現我所需要的功能。
微調後的程式碼
以下為beforeUpload改變後的程式碼:
beforeUpload(file) { let _this = this; // 使返回的值變成Promise對象,如果校驗不通過,則reject,校驗通過,則resolve return new Promise(function(resolve, reject){ // readExcel方法也使用了Promise非同步轉同步,此處使用then對返回值進行處理 _this.readExcel(file).then(result => {// 此時標識校驗成功,為resolve返回 const isLt2M = file.size / 1024 / 1024 < 2; if (!isLt2M) { _this.$message.error('文件大小不能超過2MB!'); } if (isLt2M && result){ resolve('校驗成功!'); } else { reject(false); } }, error => {// 此時為校驗失敗,為reject返回 _this.$message.error(error); reject(false); }); }); }以下為readExcel改動後的程式碼
readExcel(file) {// 解析Excel let _this = this; return new Promise(function(resolve, reject){// 返回Promise對象 const reader = new FileReader(); reader.onload = (e) => {// 非同步執行 try { // 以二進位流方式讀取得到整份excel表格對象 var data = e.target.result, workbook = XLSX.read(data, {type: 'binary'}); } catch (e) { reject(e.message); } // 表格的表格範圍,可用於判斷表頭是否數量是否正確 var fromTo = ''; // 遍歷每張表讀取 for (var sheet in workbook.Sheets) { let sheetInfos = workbook.Sheets[sheet]; let locations = [];// A1,B1,C1... if (workbook.Sheets.hasOwnProperty(sheet)) { fromTo = sheetInfos['!ref'];// A1:B5 locations = _this.getLocationsKeys(fromTo); } for (let i = 0;i < locations.length; i++) { let value = sheetInfos[locations[i]].v; if (value != i) {// 自定的校驗規則,自由實現即可 // 校驗失敗reject reject(locations[i] + ''s parameter isn't ' + i); } } // 校驗成功resolve resolve(true); } }; reader.readAsBinaryString(file); }); }執行成功
以上程式碼完美解決了之前遇到的問題,能正確校驗並返回了
總結
- 此時整個文件上傳到文件校驗整個過程就讓人很舒服。不用等很久再響應,如果還是一個校驗失敗的結果,用戶體驗就下去了。
- 使用的
Promise是關鍵,非同步轉同步,整個的lambda語法也讓程式碼很容易理解,第一次使用,感覺尤為強大。 - 作為一個常年搞後端的人,偶爾解決一個自認為有點棘手的前端問題,感覺很有成就感,Mark下。
歡迎點評~~~~~~