一、異步匯出Excel檔案
1、設計思想
用戶無需在當前頁面等待匯出結果,點擊匯出按鈕后服務端即可回傳前端提示用戶匯出處理中請到下載中心查看結果,
具體業務檔案匯出實作由后臺異步處理匯出檔案到騰訊COS存盤(有效期七天,到期自動洗掉),
用戶統一在下載中心選單欄頁面中查看匯出任務結果并下載檔案,
2、技術組件
① EasyExcel 檔案地址:https://www.yuque.com/easyexcel/doc
② Redisson延遲佇列或xxl-job定時任務 (定時更新檔案狀態為已過期)
③ 騰訊COS物件存盤
3、具體實作
① 匯出檔案記錄表
下載中心就是從這里查資料下載檔案
export_record

② 匯出狀態列舉 ExportStateEnum
public enum ExportStateEnum { FAILED(1,"失敗"), SUCCESS(2,"成功"), GOING(3,"進行中"), EXPIRED(4,"已過期"), ; private Integer value; private String msg; }
③ 異步匯出工具類 (PS:有待優化)
@Slf4j @Component public class AsyncExcelUtil { @Autowired ExportRecordFeignClient exportRecordFeignClient; @Autowired COSClient cosClient; @Autowired ThreadPoolTaskExecutor taskExecutor; Long recordId; public ResponseData asyncExport(UserInfo userInfo, String fileName, Runnable r) { //1、資料庫初始化操作記錄 ResponseData<Long> initResult = this.exportRecordInit(userInfo, fileName); if (Objects.nonNull(initResult) && Objects.equals(initResult.getCode(), ResponseEnum.SUCCESS.getCode())) { this.recordId = initResult.getData(); taskExecutor.execute(r); return ResponseData.success("操作成功"); } return ResponseData.fail("操作失敗"); } /** * 查詢當前用戶下匯出檔案記錄資料 * * @param entity */ public ResponseData<List<ExportRecordEntity>> queryExportRecordList(ExportRecordEntity entity) { return exportRecordFeignClient.queryExportRecordList(entity); } /** * 初始化匯入匯出記錄表 * * @param userInfo * @param fileName */ public ResponseData<Long> exportRecordInit(UserInfo userInfo, String fileName) { //1、資料庫初始化操作記錄 ExportRecordEntity exportRecordEntity = new ExportRecordEntity(); exportRecordEntity.setTenantId(Long.parseLong(userInfo.getUniversityId())); exportRecordEntity.setOpType(1); exportRecordEntity.setProgress(30); exportRecordEntity.setIsSuccess(2); exportRecordEntity.setExportFileName(fileName); exportRecordEntity.setCreatedId(Long.parseLong(userInfo.getEmployeeId())); exportRecordEntity.setCreatedName(userInfo.getUserName()); exportRecordEntity.setCreatedTime(LocalDateTime.now()); exportRecordEntity.setUpdatedTime(exportRecordEntity.getCreatedTime()); return exportRecordFeignClient.exportInit(exportRecordEntity); } /** * 資料整理完畢更新進度 * * @param recordId */ public boolean exportDataComplete(Long recordId) { ExportRecordEntity exportRecordEntity = new ExportRecordEntity(); exportRecordEntity.setId(recordId); exportRecordEntity.setProgress(80); exportRecordEntity.setUpdatedTime(LocalDateTime.now()); return exportRecordFeignClient.exportDataComplete(exportRecordEntity); } /** * 匯出excel檔案上傳到騰訊COS并更新匯出操作結果 * * @param data 資料串列 * @param fileNm 檔案名 * @param sheetNm excel檔案sheet名稱 * @param <T> */ public <T> boolean exportToCos(List<T> data, String fileNm, String sheetNm) { Either<String, String> exportResult = asyncExport1(recordId, data, fileNm, sheetNm); ExportRecordEntity exportRecordEntity = new ExportRecordEntity(); exportRecordEntity.setId(recordId); exportRecordEntity.setUpdatedTime(LocalDateTime.now()); if (exportResult.isLeft()) { exportRecordEntity.setIsSuccess(0); exportRecordEntity.setFailReason(exportResult.getLeft()); } else { exportRecordEntity.setProgress(100); exportRecordEntity.setIsSuccess(1); exportRecordEntity.setExportFileUrl(exportResult.get()); } return exportRecordFeignClient.exportSaveResult(exportRecordEntity); } /** * 匯出excel檔案上傳到騰訊COS * * @param data 資料串列 * @param fileNm 檔案名 * @param sheetNm excel檔案sheet名稱 * @param <T> */ public <T> Either<String, String> asyncExport1(Long recordId, List<T> data, String fileNm, String sheetNm) { if (Objects.isNull(data) || CollectionUtils.isEmpty(data)) { return Either.left("資料為空"); } else { this.exportDataComplete(recordId); } String filePath = ""; try { //匯出操作 String basePath = ResourceUtils.getURL("classpath:").getPath() + "static/"; // 建立新的檔案 File fileExist = new File(basePath); // 檔案夾不存在,則新建 if (!fileExist.exists()) { fileExist.mkdirs(); } String fileName = fileNm + "-" + System.currentTimeMillis() + ".xlsx"; filePath = basePath + fileName; EasyExcel.write(filePath, data.get(0).getClass()).sheet(sheetNm).doWrite(data); // 指定要上傳的檔案 File localFile = new File(filePath); // 指定檔案將要存放的存盤桶 String bucketName = Constants.DOCUMENT_BUCKET; // 指定檔案上傳到 COS 上的路徑,即物件鍵,例如物件鍵為folder/picture.jpg,則表示將檔案 picture.jpg 上傳到 folder 路徑下 String key = "temp/asyncexcel/" + fileName; PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, localFile); PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest); return Either.right(cosClient.getObjectUrl(bucketName, key).toString()); } catch (Exception e) { log.error("異步匯出excel例外:", e); return Either.left(e.getMessage()); } finally { removeTempFile(filePath); } } /***************************** private私有方法 *********************************/ private static void removeTempFile(String filePath) { File delFile = new File(filePath); if (delFile.exists()) { delFile.delete(); } } }
4、AsyncExcelUtil工具類具體使用示例
@PostMapping(value = "https://www.cnblogs.com/testAsyncExport", produces = {"application/json"})
public ResponseData testAsyncExport(@RequestBody CommonRequestParam param) {
UserInfo userInfo = param.getUserInfo();
String fileName = "異步匯出測驗檔案";
UserInfoParam userParam = new UserInfoParam();
userParam.setUserName(userInfo.getUserName());
userParam.setUserId(userInfo.getUserId());
userParam.setModule("各自業務模塊名稱,如:直播資料");
return asyncExcelUtil.asyncExport(userParam, fileName, () -> {
//模擬封裝得到要匯出的資料
List<ExportVo> retVo = new ArrayList<>();
for (int i = 0; i < 7000; i++) {
ExportVo vo = new ExportVo();
vo.setModule("商城");
vo.setName("張三" + i);
vo.setUserDept("部門" + i);
vo.setWatchTotal("20");
retVo.add(vo);
}
asyncExcelUtil.exportToCos(userParam, retVo, fileName, "直播測驗資料");
});
}
引數說明
① userParam 記錄操作人資訊
② fileName 檔案名稱
③ 行為引數化 不需要每個業務再創建對應的實作類,Lamda運算式代替內名內部類實作靈活,Runnable 接收傳入執行緒池異步執行,
5、優化點:子執行緒例外處理
由于上面的實作如果異步子執行緒中發生例外是會直接退出的,無法記錄任何日志,如果讓業務方自己添加try catch模塊有可能會造成疏漏而且也不方便,
優化方案:為執行緒設定“未捕獲例外處理器”UncaughtExceptionHandler
在子執行緒中多添加一行固定代碼設定當前執行緒的例外處理器:
Thread.currentThread().setUncaughtExceptionHandler(new CustomThreadExceptionHandler());
public class CustomThreadExceptionHandler implements Thread.UncaughtExceptionHandler{ @Override public void uncaughtException(Thread t,Throwable e) { //處理 記錄到庫資料獲取例外 } }
6、異步匯入Excel方案
實作思路整合和異步匯出一致,在下載中心串列中區分匯入和匯出的操作,并且匯入操作須記錄能夠直接跳轉到對應業務選單頁面去,能夠下載匯入錯誤資料的excel檔案,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/456978.html
標籤:Java
上一篇:求求你不要寫滿屏的 try...catch 了,這才是優雅的處理方式,真香!
下一篇:使用easyExcel注意事項
