導出excel報表之類,相信有過1~2年開發經驗的至少都做過了。但是大多應該都是傳統的SSH或SSM架構,相對於在最近流行的SpringCloud分佈式架構上做類似導出,可能經歷不是那麼多。
鄙人做過的導出excel報表,有2種方案:
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.9</version> </dependency>
poi方法參考另一篇博文:poi方法導出excel文檔HSSFWorkbook、HSSFSheet、HSSFCellStyle等一系列,poi包中類庫。優勢:可以根據業務需要最大限度的格式化excel報表(合併單元格/居中/字體顏色等);此種方案見另一篇資料: poi格式導出excel
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-csv</artifactId> <version>1.4</version> </dependency>
由於本人項目中,之前統一了導出方案由:CSVPrinter格式,也妥協於api-zuul網關的輸入輸出流格式的限制,最終選用此種。優勢:可與查詢接口合併,查詢數據供前端頁面調用,導出直接輸出excel文檔。
今天跟大家分享一下,在SpringCloud架構下,導出excel報表的經驗。同時又錯誤或遺漏之處歡迎指正建議,感激。
xml報文內容格式:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <Root> <SttlCntNb>2</SttlCntNb> <DebitCntAmt>CNY0.00</DebitCntAmt> <CreditCntAmt>CNY4700.00</CreditCntAmt> <SttlList> <SttlInf> <SttlReptFlg>2018052500170139</SttlReptFlg> <SttlDCFlg>2</SttlDCFlg> <SttlAmt>CNY100.00</SttlAmt> <BatchList> <BatchInf> <BatchId>B201805230015</BatchId> <BatchDCFlg>2</BatchDCFlg> <BatchNetAmt>CNY100.00</BatchNetAmt> <SubItemList> <SubItemInf>0113|C3228644000018|04|CNY0.00|0|CNY5.00|1|</SubItemInf> <SubItemInf>0120|C3228640000020|05|CNY50.00|1|CNY0.00|0|</SubItemInf> <SubItemInf>0114|C3228640000029|07|CNY0.00|0|CNY5.00|1|</SubItemInf> <SubItemInf>0115|C3228640000019|06|CNY0.00|0|CNY5.00|1|</SubItemInf> <SubItemInf>0111|C3228640000016|03|CNY0.00|0|CNY10.00|1|</SubItemInf> <SubItemInf>0110|C3228644000016|01|CNY0.00|0|CNY110.00|1|</SubItemInf> <SubItemInf>0112|C3228644000017|99|CNY0.00|0|CNY5.00|1|</SubItemInf> <SubItemInf>0110|C3228644000016|02|CNY0.00|0|CNY10.00|1|</SubItemInf> </SubItemList> </BatchInf> </BatchList> </SttlInf> <SttlInf> <SttlReptFlg>2018052500170138</SttlReptFlg> <SttlDCFlg>2</SttlDCFlg> <SttlAmt>CNY4600.00</SttlAmt> <BatchList> <BatchInf> <BatchId>B201805240001</BatchId> <BatchDCFlg>2</BatchDCFlg> <BatchNetAmt>CNY400.00</BatchNetAmt> <SubItemList> <SubItemInf>0126|C1010511003703|00|CNY0.00|4|CNY0.00|0|</SubItemInf> <SubItemInf>0125|C1010211000012|01|CNY0.00|8|CNY0.00|0|</SubItemInf> <SubItemInf>0126|C1010211000012|01|CNY0.00|8|CNY0.00|0|</SubItemInf> <SubItemInf>0124|C1010211000012|01|CNY0.00|8|CNY0.00|0|</SubItemInf> <SubItemInf>0123|C1010211000012|01|CNY0.00|4|CNY0.00|0|</SubItemInf> <SubItemInf>0110|C1010211000012|01|CNY0.00|0|CNY400.00|2|</SubItemInf> </SubItemList> </BatchInf> <BatchInf> <BatchId>B201805240002</BatchId> <BatchDCFlg>2</BatchDCFlg> <BatchNetAmt>CNY400.00</BatchNetAmt> <SubItemList> <SubItemInf>0125|C1010211000012|00|CNY400.00|4|CNY0.00|0|</SubItemInf> <SubItemInf>0110|C1010211000012|00|CNY0.00|0|CNY800.00|2|</SubItemInf> </SubItemList> </BatchInf> <BatchInf> <BatchId>B201805240003</BatchId> <BatchDCFlg>1</BatchDCFlg> <BatchNetAmt>CNY0.00</BatchNetAmt> <SubItemList> <SubItemInf>0120|C1010211000012|00|CNY0.00|4|CNY0.00|0|</SubItemInf> </SubItemList> </BatchInf> <BatchInf> <BatchId>B201805240004</BatchId> <BatchDCFlg>2</BatchDCFlg> <BatchNetAmt>CNY1200.00</BatchNetAmt> <SubItemList> <SubItemInf>0112|C1010211000012|01|CNY0.00|0|CNY1200.00|2|</SubItemInf> </SubItemList> </BatchInf> <BatchInf> <BatchId>B201805240005</BatchId> <BatchDCFlg>2</BatchDCFlg> <BatchNetAmt>CNY400.00</BatchNetAmt> <SubItemList> <SubItemInf>0113|C1010211000012|00|CNY0.00|0|CNY400.00|4|</SubItemInf> </SubItemList> </BatchInf> <BatchInf> <BatchId>B201805240006</BatchId> <BatchDCFlg>2</BatchDCFlg> <BatchNetAmt>CNY400.00</BatchNetAmt> <SubItemList> <SubItemInf>0114|C1010211000012|00|CNY0.00|0|CNY400.00|4|</SubItemInf> </SubItemList> </BatchInf> <BatchInf> <BatchId>B201805240007</BatchId> <BatchDCFlg>2</BatchDCFlg> <BatchNetAmt>CNY200.00</BatchNetAmt> <SubItemList> <SubItemInf>0115|C1010211000012|01|CNY0.00|0|CNY200.00|2|</SubItemInf> </SubItemList> </BatchInf> <BatchInf> <BatchId>B201805240008</BatchId> <BatchDCFlg>2</BatchDCFlg> <BatchNetAmt>CNY600.00</BatchNetAmt> <SubItemList> <SubItemInf>0115|C1010211000012|01|CNY0.00|0|CNY600.00|2|</SubItemInf> </SubItemList> </BatchInf> <BatchInf> <BatchId>B201805240009</BatchId> <BatchDCFlg>2</BatchDCFlg> <BatchNetAmt>CNY400.00</BatchNetAmt> <SubItemList> <SubItemInf>0110|C1010511003703|00|CNY0.00|0|CNY400.00|2|</SubItemInf> </SubItemList> </BatchInf> <BatchInf> <BatchId>B201805240010</BatchId> <BatchDCFlg>2</BatchDCFlg> <BatchNetAmt>CNY1200.00</BatchNetAmt> <SubItemList> <SubItemInf>0111|C1010211000012|01|CNY0.00|0|CNY1200.00|4|</SubItemInf> </SubItemList> </BatchInf> <BatchInf> <BatchId>B201805240012</BatchId> <BatchDCFlg>1</BatchDCFlg> <BatchNetAmt>CNY1200.00</BatchNetAmt> <SubItemList> <SubItemInf>0116|C1010511003703|00|CNY1200.00|4|CNY0.00|0|</SubItemInf> </SubItemList> </BatchInf> </BatchList> </SttlInf> </SttlList> </Root>
可以看到嵌套了好幾層:SttlList—>SttlInf—>BatchList—>BatchInf—>SubItemList—>SubItemInf
其中,要先把xml報文解析成obj對象,根據xml報文拆分後封裝的obj對象,對於xml報文解析可查看另一篇:
核心類庫是:
1、org.apache.commons.csv 類庫中的CSVPrinter進行打印輸出。
2、藉助java.io的輸入輸出流等
HttpServletRequest
HttpServletResponse
OutputStream/OutputStreamWriter
3、org.apache.commons.io關閉CSVPrinter打印輸出流。
上代碼:接口Controller控制入口類:
@RequestMapping(value = "/export") @Logable(businessTag = "accountInfoExport") @Exceptionable @Validatable @ApiOperation(value = "對賬文件導出", notes = "對賬文件導出", httpMethod = "GET") @ApiImplicitParams({ @ApiImplicitParam(name = "accountDate", value = "對賬日期(yyyy-MM-dd)", required = true, dataType = "String", length = 100, paramType = "query"), @ApiImplicitParam(name = "download", value = "是否下載:true/false", required = true, dataType = "boolean", length = 100, paramType = "query"), @ApiImplicitParam(name = "fileName", value = "例:AccountInfo", required = true, dataType = "String", length = 100, paramType = "query"), @ApiImplicitParam(name = "type", value = "格式:csv", required = true, dataType = "String", length = 100, paramType = "query")}) public PageResult<SettInfoExportDto> exportAccountInfo(@RequestParam String accountDate, @RequestParam(value = "download", required = true) boolean download, @RequestParam(value = "fileName", required = true) String fileName, @RequestParam(value = "type", required = true) String type, HttpServletRequest request, HttpServletResponse response) { PageResult<SettInfoExportDto> pageResult = new PageResult<SettInfoExportDto>(); AccountInfoEntityResp accountInfoEntityResp = accountInfoService.getLocalAccountInf(accountDate); //1.結算場次列表 List<String> settListTitles = new ArrayList<>(); settListTitles.add("報文標識號"); settListTitles.add("場次借貸標識"); settListTitles.add("場次金額"); //2.批次列表 List<String> batchInfListTitles = new ArrayList<>(); batchInfListTitles.add("報文標識號"); batchInfListTitles.add("批次號"); batchInfListTitles.add("批次借貸標識"); batchInfListTitles.add("批次金額"); //3.分項列表 List<String> sttlInfListTitles = new ArrayList<>(); sttlInfListTitles.add("批次號"); sttlInfListTitles.add("業務類型"); sttlInfListTitles.add("銀行金額機構標識"); sttlInfListTitles.add("賬戶類型"); sttlInfListTitles.add("分項借方發生額"); sttlInfListTitles.add("分項借方發生筆數"); sttlInfListTitles.add("分項貸方發生額"); sttlInfListTitles.add("分項貸方發生筆數"); //4.賬務日期... Map<String, String> statisticsData = new HashMap<>(); String settCount = accountInfoEntityResp.getSttlCntNb().toString(); String debitAmount = accountInfoEntityResp.getDebitCntAmt().toString(); String creditAmount = accountInfoEntityResp.getCreditCntAmt().toString(); statisticsData.put("財務日期:", accountDate.replace("//-","///")); statisticsData.put("結算總筆數", settCount); statisticsData.put("借方金額", debitAmount); statisticsData.put("貸方金額", creditAmount); //5.結算場次列表數據list List<SettInfoExportDto> settInfoExportDtoList = new ArrayList<SettInfoExportDto>(); //6.批次列表數據list List<BatchInfExportDto> batchInfExportDtoList = new ArrayList<BatchInfExportDto>(); //7.分項列表數據list List<SubItemInfoExportDto> subItemInfoExportDtoList = new ArrayList<SubItemInfoExportDto>(); if(accountInfoEntityResp.getSttlList() != null){ //結算場次列表數據 for(int i=0; i<accountInfoEntityResp.getSttlList().size(); i++){ SettInfoExportDto settInfoExportDto = new SettInfoExportDto(); settInfoExportDto.setSttlAmt(accountInfoEntityResp.getSttlList().get(i).getSttlAmt()); settInfoExportDto.setSttlDCFlg(accountInfoServiceImpl.DCFlag(accountInfoEntityResp.getSttlList().get(i).getSttlDCFlg())); settInfoExportDto.setSttlReptFlg(accountInfoEntityResp.getSttlList().get(i).getSttlReptFlg()); settInfoExportDtoList.add(settInfoExportDto); //批次列表數據 if(accountInfoEntityResp.getSttlList().get(i).getBatchList() != null){ for(int j= 0; j<accountInfoEntityResp.getSttlList().get(i).getBatchList().size(); j++) { BatchInfExportDto batchInfExportDto = new BatchInfExportDto(); batchInfExportDto.setSttlReptFlg(accountInfoEntityResp.getSttlList().get(i).getSttlReptFlg()); batchInfExportDto.setBatchDCFlg(accountInfoEntityResp.getSttlList().get(i).getBatchList().get(j).getBatchDCFlg()); batchInfExportDto.setBatchId(accountInfoEntityResp.getSttlList().get(i).getBatchList().get(j).getBatchId()); batchInfExportDto.setBatchNetAmt(accountInfoEntityResp.getSttlList().get(i).getBatchList().get(j).getBatchNetAmt()); batchInfExportDtoList.add(batchInfExportDto); //分項列表數據 if(accountInfoEntityResp.getSttlList().get(i).getBatchList().get(j).getSubItemList() != null){ for(int f=0; f< accountInfoEntityResp.getSttlList().get(i).getBatchList().get(j).getSubItemList().size(); f++){ SubItemInfoExportDto subItemInfoExportDto = new SubItemInfoExportDto(); subItemInfoExportDto.setBatchId(accountInfoEntityResp.getSttlList().get(i).getBatchList().get(j).getBatchId()); String subStr = accountInfoEntityResp.getSttlList().get(i).getBatchList().get(j).getSubItemList().get(f).getSubItemInf(); subStr = subStr.replace("CNY",""); String[] subArray = {}; subArray = subStr.split("\\|"); subItemInfoExportDto.setBusinessType(accountInfoServiceImpl.busiType(subArray[0])); subItemInfoExportDto.setBankOrgFlag(subArray[1]); subItemInfoExportDto.setAccountType(accountInfoServiceImpl.accountType(subArray[2])); subItemInfoExportDto.setDebitSplitAmount(subArray[3]); subItemInfoExportDto.setDebitSplitCount(Integer.valueOf(subArray[4])); subItemInfoExportDto.setCreditSplitAmount(subArray[5]); subItemInfoExportDto.setCreditSplitCount(Integer.valueOf(subArray[6])); subItemInfoExportDtoList.add(subItemInfoExportDto); } } } } } } settListTitles.addAll(batchInfListTitles); settListTitles.addAll(sttlInfListTitles); CSVPrinter csvPrinter = accountInfoService.downloadCsv(request, response, settListTitles); pageResult.setTitles(settListTitles); pageResult.setResult(Constants.detailReturnCode.RETURN_SUCCESS.code); pageResult.setRows(settInfoExportDtoList); accountInfoService.printlnCsv(csvPrinter,statisticsData,settInfoExportDtoList,batchInfExportDtoList, subItemInfoExportDtoList,settListTitles,batchInfListTitles,sttlInfListTitles); IOUtils.closeQuietly(csvPrinter); return null; }
輸入輸出流,封裝CSVPrinter對象:
public CSVPrinter downloadCsv(HttpServletRequest request, HttpServletResponse response, List<String> titles) { try { // 獲取數據 String fileName = request.getParameter("fileName"); // 設置文件名, 用於下載 response.setContentType("application/csv;charset=UTF-8"); response.addHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, "utf-8")); // 添加UTF-8的BOM頭,避免Excel打開文件時使用系統默認編碼產生亂碼 byte[] byteBOM = new byte[] { (byte) 0xEF, (byte) 0xBB, (byte) 0xBF }; OutputStream outputStream = response.getOutputStream(); outputStream.write(byteBOM); OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, "utf-8"); CSVPrinter printer = CSVFormat.DEFAULT.withHeader(titles.toArray(new String[titles.size()])).print(outputStreamWriter); return printer; } catch (Exception e) { e.printStackTrace(); return null; } }
csv導出記錄:
public void printlnCsv(CSVPrinter printer, Map<String, String> statisticsData,List<SettInfoExportDto> settInfoExportDtoList, List<BatchInfExportDto> batchInfExportDtoList,List<SubItemInfoExportDto> subItemInfoExportDtoList, List<String> settListTitles,List<String> batchInfListTitles,List<String> sttlInfListTitles){ if(subItemInfoExportDtoList.size() > 0){ try{ //報文標識號 String sttlReptFlg = null; //結算列表數據 for(SettInfoExportDto row:settInfoExportDtoList){ Field[] declaredFields = row.getClass().getDeclaredFields(); for (Field declaredField : declaredFields) { declaredField.setAccessible(true); FieldAnnotation annotation = declaredField.getAnnotation(FieldAnnotation.class); if (annotation == null) { // 只有註解了 FieldAnnotation 的字段,纔會輸出 continue; } Object value = declaredField.get(row); if (StringUtils.equals(annotation.fieldName(), "報文標識號")) { sttlReptFlg = (String) value; } printer.print(value + "\t"); } // 換行 printer.println(); //打印批次列表數據 printBacthRecord(printer,sttlReptFlg, batchInfExportDtoList, subItemInfoExportDtoList, settListTitles ,batchInfListTitles, sttlInfListTitles); } // 換行 printer.println(); //手續費行 if (statisticsData != null && !statisticsData.isEmpty()) { Iterator<String> iterator = statisticsData.keySet().iterator(); while (iterator.hasNext()) { String key = (String) iterator.next(); printer.print(key); printer.print(statisticsData.get(key)); // 換行 printer.println(); } byte[] nulls = new byte[] { (byte) 00, (byte) 00, (byte) 00 }; printer.print(new String(nulls)); } printer.flush(); }catch (Exception e) { e.printStackTrace(); } } } /** * 打印批次列表數據 */ public void printBacthRecord(CSVPrinter printer,String sttlReptFlg,List<BatchInfExportDto> batchInfExportDtoList, List<SubItemInfoExportDto> subItemInfoExportDtoList,List<String> settListTitles , List<String> batchInfListTitles,List<String> sttlInfListTitles) throws IOException { int i = settListTitles.size() - batchInfListTitles.size() - sttlInfListTitles.size(); //取出該報文標識號的批次列表 List<BatchInfExportDto> printBatchInfoList = new ArrayList<BatchInfExportDto>(); if(batchInfExportDtoList != null && batchInfExportDtoList.size()>0){ for(BatchInfExportDto record:batchInfExportDtoList){ if(record.getSttlReptFlg().equals(sttlReptFlg)){ printBatchInfoList.add(record); } } } if (printBatchInfoList != null && !printBatchInfoList.isEmpty()) { String batchId = null; for (BatchInfExportDto bacthInfo : printBatchInfoList) { byte[] nulls = new byte[i]; for (byte b : nulls) { printer.print(new String(nulls)); } printer.print(sttlReptFlg); printer.print(bacthInfo.getBatchId()); printer.print(bacthInfo.getBatchDCFlg()); printer.print(bacthInfo.getBatchNetAmt()); batchId = bacthInfo.getBatchId(); // 換行 printer.println(); //打印分項列表數據 printSubItemRecord(printer,batchId,subItemInfoExportDtoList,settListTitles,sttlInfListTitles); } } } /** * 打印分項列表數據 */ public void printSubItemRecord(CSVPrinter printer,String batchId,List<SubItemInfoExportDto> subItemInfoExportDtoList, List<String> settListTitles ,List<String> sttlInfListTitles ) throws IOException { int i = settListTitles.size() - sttlInfListTitles.size(); //取出該批次號的分項列表 List<SubItemInfoExportDto> subItemPrintRecordList = new ArrayList<SubItemInfoExportDto>(); if(subItemInfoExportDtoList != null && subItemInfoExportDtoList.size()>0){ for(SubItemInfoExportDto record : subItemInfoExportDtoList){ if(record.getBatchId().equals(batchId)){ subItemPrintRecordList.add(record); } } } if (subItemPrintRecordList != null && !subItemPrintRecordList.isEmpty()) { for (SubItemInfoExportDto subItemInfo : subItemPrintRecordList) { byte[] nulls = new byte[i]; for (byte b : nulls) { printer.print(new String(nulls)); } printer.print(batchId); printer.print(subItemInfo.getBusinessType()); printer.print(subItemInfo.getBankOrgFlag()); printer.print(subItemInfo.getAccountType()); printer.print(subItemInfo.getDebitSplitAmount()); printer.print(subItemInfo.getDebitSplitCount()); printer.print(subItemInfo.getCreditSplitAmount()); printer.print(subItemInfo.getDebitSplitCount()); // 換行 printer.println(); } } }
主要核心代碼以上。
最終導出效果如下: