系統環境:
Java JDK:1.8.0_202
Node.js:v12.2.0
Npm:6.9.0
Java后端實現
Controller
/*** xxxx-導出* @param response 返回信息體* @param files 上傳的圖片文件* @param param1 參數1* @param param2 參數2*/@PostMapping("/exportXX")@ApiOperationSupport(order = 13)@ApiOperation(value = "導出Excel", notes = "導出Excel")@ApiLog("XXX導出")public void exportXX(HttpServletResponse response, @RequestParam("files") MultipartFile[] files, @RequestParam("param1") String param1,@RequestParam(value = "param2",defaultValue = "1") String param2) {tableExportService.exportXX(response, files, param1, param2);}
Service
/*** XXX-導出*/void exportXX(HttpServletResponse response, MultipartFile[] files, String param1,String param2);
Impl
/*** xxxx-導出* @param response 返回信息體* @param files 上傳的圖片文件* @param param1 參數1* @param param2 參數2*/@Overridepublic void exportXX(HttpServletResponse response,MultipartFile[] files, String param1, String param2) {try {// 返回信息體重置response.reset();// 設置類型response.setContentType("application/force-download");// 賦值壓縮包名稱及頭部信息SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String fileName = "attachment;filename=cryExcel" + format.format(new Date()) + ".zip";response.setHeader("Content-Disposition", fileName);// 發送二進制數據到客戶端的輸出流ServletOutputStream servletOutputStream = response.getOutputStream();ZipOutputStream zipOut = new ZipOutputStream(servletOutputStream);// 圖片添加到ZIPaddPicToZip(files,zipOut);// 表頭List<List<String>> headList = new ArrayList<>();// 固定列headList.add(Arrays.asList("列1"));headList.add(Arrays.asList("列名2"));headList.add(Arrays.asList("這是列名3"));// 導出數據List<List<String>> dataList = new ArrayList<>();// 自行獲取需要導出為excel的數據信息List<Map<String, Object>> list = ……;// 列表不為空時按照列1進行排序if(Func.isNotEmpty(list)){list.sort(Comparator.comparing(map -> (String) map.get("列1的鍵")));}// 轉換數據格式為二維數組 方便存入excelfor (Map<String, Object> item : list) {dataList.add(Arrays.asList(String.valueOf(item.getOrDefault("列1的值", "")),String.valueOf(item.getOrDefault("列2的值", "")),String.valueOf(item.getOrDefault("列3的值", "")) + "%"));}// 導出excel 并合并第一列的相同內容int[] mergeColumeIndex = {0};int mergeRowIndex = 1;String excelName = "導出excel.xlsx";File excelfile = new File(excelName);if (!excelfile.exists()) {excelfile.createNewFile();}// 將excel寫入壓縮包EasyExcel.write(excelName).head(headList).registerWriteHandler(new ExcelFillCellLineMergeHandler(mergeRowIndex, mergeColumeIndex)).sheet("導出excel").doWrite(dataList);// 創建 ZipEntryZipEntry entry = new ZipEntry(excelName);zipOut.putNextEntry(entry);// 讀取文件并寫入 ZipOutputStreamtry (FileInputStream fis = new FileInputStream(excelfile)) {byte[] buffer = new byte[1024];int length;while ((length = fis.read(buffer)) > 0) {zipOut.write(buffer, 0, length);}}// 關閉當前的 ZipEntryzipOut.closeEntry();// 關流zipOut.close();} catch (Exception e){e.printStackTrace();}}/*** 將Base64編碼的圖片文件添加到ZIP輸出流中* @param files 包含Base64編碼的圖片的MultipartFile數組* @param zipOut 目標ZIP輸出流*/
private void addPicToZip(MultipartFile[] files, ZipOutputStream zipOut) {try {// 檢查文件數組是否為空if (files != null) {// 遍歷所有文件for (MultipartFile file : files) {// 從MultipartFile中獲取輸入流并轉換為字符串String imageData = StreamUtils.copyToString(file.getInputStream(), StandardCharsets.UTF_8);// 移除Base64數據前綴(如果存在)imageData = imageData.replace("data:image/png;base64,", "");// 解碼Base64字符串為字節數組byte[] imageBytes = Base64.getDecoder().decode(imageData);// 創建ZIP條目,使用原始文件名并添加.png擴展名ZipEntry zipEntry = new ZipEntry(file.getOriginalFilename() + ".png");// 將條目添加到ZIP輸出流zipOut.putNextEntry(zipEntry);// 寫入圖片字節數據到ZIP條目zipOut.write(imageBytes, 0, imageBytes.length);}}} catch (IOException e) {// 打印異常堆棧信息e.printStackTrace();}
}
- 注:對于此處我個人覺得直接讓前端上傳file二進制文件更好,后端直接獲取file的字節碼,然后弄進壓縮包,此處可以根據業務需求自行調整~
Vue 前端實現
Api
/*** @description 測試導出* */
export const exportXX = (data) => {return request({headers: {"Content-Type": "multipart/form-data"// 指定請求體為多部分表單數據(用于文件上傳)},method: 'post',responseType: 'blob',// 指定響應類型為二進制大對象(用于接收文件流)data,url: 'http://localhost:8990/dev-api/tableExport/exportXX',})
}
Vue
此處可以自行選擇upload組件上傳的文件或者echarts圖形截圖等文件進行上傳
// 上傳圖片信息async uploadImages() {try {// 創建FormDataconst formData = new FormData();if (this.selectedFiles.length !== 0) {// 處理每個文件for (const file of this.selectedFiles) {// 轉換為完整的Base64 DataURL(包含前綴)const base64DataUrl = await this.convertToBase64(file);// 創建一個Blob對象,內容為Base64字符串(作為文本)const blob = new Blob([base64DataUrl], { type: "text/plain" });// 使用原始文件名創建File對象const fileToUpload = new File([blob], file.name, {type: "text/plain",});// 添加到FormDataformData.append("files", fileToUpload);}}formData.append("param1", "111");formData.append("param2", "222");// 調用導出接口await exportXX(formData).then((res) => {console.log(formData);console.log(res);if(res){const elink = document.createElement('a');elink.download = '文件名稱.zip';elink.style.display = 'none';const blob = new Blob([res], { type: 'application/x-msdownload' });elink.href = URL.createObjectURL(blob);document.body.appendChild(elink);elink.click();document.body.removeChild(elink);}else {this.$message.error('導出異常請聯系管理員');}});console.log("上傳成功");} catch (error) {console.error("上傳錯誤:", error);}},//轉換為base64字符convertToBase64(file) {return new Promise((resolve, reject) => {const reader = new FileReader();reader.onload = () => resolve(reader.result); // 返回完整的DataURL(含前綴)reader.onerror = reject;reader.readAsDataURL(file);});},