SpringCloud把xml報文導出Excel(csv格式)文檔

    導出excel報表之類,相信有過1~2年開發經驗的至少都做過了。但是大多應該都是傳統的SSH或SSM架構,相對於在最近流行的SpringCloud分佈式架構上做類似導出,可能經歷不是那麼多。

  鄙人做過的導出excel報表,有2種方案:

  1.  Poi原生的,jar類庫
    <dependency>
    			<groupId>org.apache.poi</groupId>
    			<artifactId>poi</artifactId>
    			<version>3.9</version>
    		</dependency>
    poi方法參考另一篇博文:poi方法導出excel文檔HSSFWorkbook、HSSFSheet、HSSFCellStyle等一系列,poi包中類庫。優勢:可以根據業務需要最大限度的格式化excel報表(合併單元格/居中/字體顏色等);此種方案見另一篇資料:
    poi格式導出excel
  2. apache系列的commons-csv類庫:CSVPrinter
    <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報文解析可查看另一篇:

Dom4j解析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();
            }
        }
    }

主要核心代碼以上。

最終導出效果如下: