這幾天一直在做報表模塊。做報表的過程中,需要上傳本地CSV格式文件,以供后端解析,從而批量導入數據;同時,也需要從后端下載文件(格式多種:有CSV,PDF,以及JSON),用於本地瀏覽。
上傳文件:文件的上傳基本都是采用 <input type="file" id="upload_files" name="upload_files"/>。通過 type="file"打開本地文件夾進行文件的選取(期間這個原理,沒鬧明白),文件選取完成后,就可以直接提交form表單或者獲取文件數據發送后端了。這期間試過幾種方式,最后選了一個。
<!--第一種:直接form表單提交。這種方式好處:在於不需要另外編寫數據提交方法,簡單、快速; 壞處:在於form表單提交后,頁面會跳轉到form表單的action鏈接上,雖然自動跳轉可以避免,但是還是需要另外編寫方法,同時數據不好處理--> <form id="uploadForm" action="http://192.168.0.120:9090/lampinfo/device/importCsv" enctype="multipart/form-data" method="post"> <input type="file" id="upload_files" name="upload_files"/> <input id="data_upload_button" type="submit" value="Upload"/> </form>
<!--第二種:將文件的選擇和數據提交分開。這種方式好處:文件選擇后,在文件選擇的后面,能清楚知道選擇的是哪個文件。 數據單獨提交,便於對數據的封裝和處理。同時在采用ajax異步提交數據后,界面不會切換;壞處:在於文件選擇、數據提交是分開進行,在操作上有些繁瑣。--> <form id="uploadForm" enctype="multipart/form-data" style="width: 45px;height: 26px"> <input type="file" id="upload_files" name="upload_files"/> </form> <button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-expanded="false" ng-click="saveReport()"> <i class="fa fa-cloud-upload" style="font-size: 14px"></i> </button> /*** * 功能:文件上傳,格式不限 * 說明:FormData 對象是XMLHttpRequest 2級定義的,它為序列化表單以及創建與表單格式相同的數據(當然是用於XHR傳輸)提供便利。 * 利用 FormData 對象,可以通過對象鍵值對來模擬一系列表單格式的數據,以前上傳文件需要用form表單封裝起來(即和后端約定一個數據傳輸格式),現在FormData對象就是按照規定的格式,把form表單中所有表單元素的name與value組裝成一個queryString,省去了手工拼接的工作。
* JQ里面有個表單序列化的函數,FormData對象的作用和它是一樣的。 * FormData對象還提供了更多的操作方法,但全部都在其原型對象中,自己本身沒任何的屬性及方法。同時還可以使用 XMLHttpRequest的send()方法來異步提交表單。與普通的Ajax相比,使用FormData的最大優點就是可以異步上傳二進制文件。 * 由於FormData是 XMLHttpRequest Level 2 新增的接口,現在 低於IE10 的IE瀏覽器不支持 FormData */ $scope.saveReport = function () { var formData = new FormData(); //新建一個FormData對象實例 /* 調用append方法添加數據,參數兩個,以鍵值對形式,相當於手工拼接。此處將文件添加到表單對象里*/ formData.append('upload_files', $('#upload_files')[0].files[0]); /* 也可以直接把form表單作為參數傳遞給FormData對象。new FormData的參數是一個DOM對象(即一個DOM節點),而非jQuery對象;這兩種方式也可以混用*/ var uploadForm = document.getElementById("uploadForm"); var formData = new FormData(uploadForm); formData.append('data', "fengling");
/* 注意通過JQ ajax提交數據的時候,屬性設置有講究 */ $.ajax({ url: url, type: 'POST', cache: false, //不緩存 data: formData, //上傳formData封裝的數據 processData: false, //告訴jQuery不要去處理發送的數據 contentType: false //告訴jQuery不要去設置Content-Type請求頭 }).done(function (res) { layer.msg("File upload or data interpretation failed!", { //layer.msg是 layer插件 的一個信息提示模板。此處用來提示用戶文件上傳成功 area: ['600', '70px'], icon: 1, time: 2000, //2秒關閉(如果不配置,默認是3秒) anim: 6, skin: 'customStyle' }); }).fail(function (res) { layer.msg("File upload or data interpretation failed!", { //layer.msg是 layer插件 的一個信息提示模板。此處用來提示用戶文件上傳失敗。當然這兩個layer.msg可以寫成一個方法,減少代碼冗余 area: ['600', '70px'], icon: 2, //0表示提醒,1表示成功,2表示失敗! time: 2000, //2秒關閉(如果不配置,默認是3秒) anim: 6, skin: 'customStyle' }); }); };
導出文件:前端通過模擬表單,用瀏覽器將文件導出來。當然在導出文件之前后端需要根據查詢參數查詢到正確數據后,建立一個內容格式正確的文件!
/** * 功能:導出文檔,文檔下載 * 把CSV文件上傳到后端之后,那么前端就算是完事了,至於后端怎么玩的,我們不管;接下來就是導出文件了,相比於上傳文件,導出文件就相當簡單了。 * 前端將需要查詢數據的參數發送到后端,后端根據參數將需要的數據查詢出來以后,按固定格式生成CSV,PDF等格式的文件(生成PDF文件時,如果需要展示報表的圖形界面,則前端需要將報表圖形轉換成base64位編碼發送給后端,后端自己解析編碼並完成構圖), * 接下來就是文件導出了。注意:由於ajax請求下載的時候,后端是以數據流的形式將文件數據返回回來,那么前端就無法獲取到固定格式的文件(就是一串編碼)。 * 這時候又該表單上場了,我們以模擬表單的方式,將文件導出來。模擬表單就是在頁面上新建一個from表單,然后將該表單添加到頁面的隨意一個節點下(此處直接掛載在了body下,當然它是隱藏的,界面上不會顯示),然后通過form表單的submit方法,通過瀏覽器將文件導出來。 */ $scope.export = function () { var parameter = {}; //查詢數據的參數 $http({ url: url, //查詢數據,后端生成文件的鏈接,不是下載鏈接。 method: "POST", dataType: "JSON", headers: { "Accept": "application/json", "Content-type": "application/json;charset=UTF-8" }, data: parameter, cache: false, timeout: 15000 //超時設置為15秒 }).then(function (response) { if (response.data.message == "success") { var downloadtype = "",value = ""; if ($scope.inside.exportType == "pdf") { downloadtype = "PDF"; } else { downloadtype = "CSV"; value = parameter.name + ".csv"; } $("body").find(".download").remove(); var $eleForm = $("<form method='POST' class='download'><input type='hidden' name='csvFileName'></form>"); $eleForm.attr("action", $scope.inside.STL_Inventory + "/lampinfo/deviceStatusReport/export" + downloadtype); //文件導出鏈接在這 $("body").append($eleForm); $("body").find("input[name='csvFileName']").attr("value", value); $eleForm.submit(); //通過表單submit方法,模擬表單將文件導出 } else { Confirmation.confirm("Export Failed!", 0, "OK"); } }).catch(function (response) { Confirmation.confirm("Export Failed!", 0, "OK"); }); };
在完成了文件的上傳和導出以后,接下來就是圖片的上傳和下載。
圖片的上傳和下載:前面在PDF文件的構建時有需要將報表的圖形部分上傳到后端。圖片的上傳是一樣的,就是將本地圖片獲取到以后轉換成base64位編碼,然后將編碼發送到后端,后端獲取到編碼以后存入數據庫,則圖片上傳完成。
顯示圖片,則是一個反向工程,從后端將base64編碼獲取到以后,解碼顯示成圖片就成。至於下載圖片,form表單嚴陣以待!
/** * 功能:選擇圖片,獲取本地圖片的base64位編碼 * 這應該是文件上傳的第三種方式:前兩種都是顯視 type='file' ,這一種是模擬一個表單。將type='file'封裝在方法內部,避免了type='file' 顯視顯示,出現樣式上的不協調 * 在方法內新建一個表單(當然樣式是隱視的),然后將該表單添加到頁面的隨意一個節點下(此處還是直接掛載在了body下),然后自點擊一下,通過自點擊打開 type='file', 進行圖片的選取。 * 需要注意的是:在方法的最后面需要將這個掛載上去的節點移除,不然添加的圖片永遠都是第一次添加的那張。 */ $scope.addPicture = function () { var file = $("<input type='file' name='file' id='file'>"), form = $("<form id='uploadForm' class='uploadfile' enctype='multipart/form-data' style='display: none'></form>"); file.appendTo(form); $("body").append(form); file.click(); //自點擊表單 type='file' file.change(function () { //圖片更換執行回調方法 var file = document.getElementById("file").files[0]; // if (!/image\/\w+/.test(file.type)) //判斷獲取的是否為圖片文件,jpg和png格式 // { // Confirmation.confirm("請確保文件為圖像文件!", 0, "OK"); // return false; // } var reader = new FileReader(); reader.readAsDataURL(file); //將文件讀取為DataURL,讀取的內容是加密以后的本地文件路徑 reader.onload = function (e) { //文件讀取成功完成后,執行方法;其讀取回的所有數據都在事件對象e內 $scope.inside.pictureList.push({"deviceType":"LampPoleModel","picture": e.target.result}); $("#buttonList img").eq(0).click(); }; $("body").find(".uploadfile").remove(); //在方法的最后面需要將這個掛載上去的節點移除,不然添加的圖片永遠都是第一次添加的那張 /** 這里需要介紹下FileReader對象(免得下次讀代碼,又要去度娘) * FileReader:是window對象的一個構造函數,用於讀取文件選擇標簽(type='file')選擇的File的Dom對象。即用來把文件選擇的信息讀入內存,並且讀取文件中的數據。 * 其接口提供了一個異步API,使用該API可以在瀏覽器主線程中異步訪問文件系統,讀取文件中的數據。 * 為了安全FileReader可以讀取表單上已經選擇的文件,不能讀取本地文件,它以二進制信息的方式讀取表單文件:主要用於大文件的信息讀取。 * 特點:1、讀取后,二進制信息在瀏覽器內存中,批量的向服務器進行傳輸。2、一般要配合后台程序,第三方插件共同完成,3、斷點下載和斷點上傳 * * FileReader接口有5個方法,其中4個用來讀取文件,另一個用來中斷讀取。無論讀取成功或失敗,方法並不會直接返回讀取結果,這一結果存儲在result屬性中。 * FileReader接口的方法: * 方法名 參數 描述 readAsArrayBuffer file 將文件讀取為一個ArrayBuffer對象以表示所讀取文件的內容. readAsBinaryString file 將文件讀取為二進制編碼 readAsText file,[encoding] 將文件讀取為文本 readAsDataURL file 將文件讀取為DataURL,讀取的內容是加密以后的本地文件路徑 abort (none) 終端讀取操作 * * FileReader接口事件:FileReader接口包含了一套完整的事件模型,用於捕獲讀取文件時的狀態。 FileReader接口的事件: 事件 描述 onabort 中斷 onerror 出錯 onloadstart 開始 onprogress 正在讀取 onload 成功讀取 onloadend 讀取完成,無論成功失敗 */ }); };