java poi動態生成單個或多個excel文件及數據渲染


最近項目上,需要使用動態數據源來生成excel數據文件,需支持單個sheet頁或者多個sheet頁導出。在此記錄下通過java poi實現的方法,有些方法在百度很容易搜到,但大部分實現時,還是有些問題。在此記錄完整的實現方法。

引入poi相關maven依賴


<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>3.17</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-scratchpad</artifactId>
    <version>3.17</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.17</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml-schemas</artifactId>
    <version>3.17</version>
</dependency>
<dependency>
    <groupId>net.sf.jxls</groupId>
    <artifactId>jxls-core</artifactId>
    <version>1.0.3</version>
</dependency>

代碼實現

excel模版動態生成方法

//列對象

public class ExcelColumnVO {

    /**
     * 列名code
     */
    private String columnCode;
    /**
     * 列名描述
     */
    private String columnDesc;
    /**
     * 列序號
     */
    private Integer seq;

}

/**
 * 創建臨時excel單個sheet頁模版文件
 * @param tempPath 模版預存路徑
 * @param columnList excel列名數組
 * @return
 * @throws IOException
 */
public static File createSingleSheetTempExcel(String tempPath, List<ExcelColumnVO> columnList) throws IOException {
    Map<String, List<ExcelColumnVO>> columnMoreSheetMap = new HashMap<>();
    columnMoreSheetMap.put(EXCEL_DEFAULT_SHEET_NAME, columnList);
    return createMoreSheetTempExcel(tempPath, columnMoreSheetMap);
}

/**
 * 創建臨時excel多個sheet頁模版文件
 * @param tempPath 模版路徑
 * @param columnMoreSheetMap 每個sheet頁列名屬性,每個sheet頁map key值需指定為data1,data2,data3...
 * @return
 * @throws IOException
 */
public static File createMoreSheetTempExcel(String tempPath, Map<String, List<ExcelColumnVO>> columnMoreSheetMap) throws IOException {
    XSSFWorkbook workbook = new XSSFWorkbook();
    // 創建一個目錄和文件名
    File tempFile = new File(tempPath);
    FileOutputStream out = new FileOutputStream(tempFile);
    Integer i = 1;
    for(Map.Entry<String, List<ExcelColumnVO>> entry : columnMoreSheetMap.entrySet()){
        String sheetName = entry.getKey();
        List<ExcelColumnVO> columnList = entry.getValue();
        // 創建一個工作表
        XSSFSheet spreadsheet = workbook.createSheet(sheetName);
        // 創建一行
        XSSFRow firstRow = spreadsheet.createRow(0);
        XSSFRow twoRow = spreadsheet.createRow(2);
        AtomicInteger firstCellid = new AtomicInteger();
        AtomicInteger secondCellid = new AtomicInteger();

        //設置模版行字段
        List<ExcelColumnVO> columnSortList = columnList.stream()
                .sorted(Comparator.comparing(ExcelColumnVO :: getSeq))
                .collect(Collectors.toList());
        int headerCellCount = 0;
        for (ExcelColumnVO columnVO : columnSortList) {
            String columnDesc = columnVO.getColumnDesc();
            spreadsheet.setColumnWidth(firstCellid.getAndIncrement(), columnDesc.getBytes().length * 2 * 256);
            Cell firstCell = firstRow.createCell(headerCellCount);
            firstCell.setCellValue(columnDesc);
            Cell secondCell = twoRow.createCell(secondCellid.getAndIncrement());
            secondCell.setCellValue("${data." + columnVO.getColumnCode() + "}");
            headerCellCount ++;
        }

        XSSFCellStyle style6 = workbook.createCellStyle();
        style6.setAlignment(HorizontalAlignment.CENTER);
        XSSFRow row1 = spreadsheet.createRow(1);
        Cell cell = row1.createCell(0);
        cell.setCellValue("<jx:forEach items = \"${data" + i + "}\" var=\"data\">");
        cell.setCellStyle(style6);
        CellRangeAddress region = new CellRangeAddress(1, 1, 0, firstCellid.intValue());
        spreadsheet.addMergedRegion(region);

        XSSFRow thirdRow = spreadsheet.createRow(3);
        Cell thirdCell = thirdRow.createCell(0);
        thirdCell.setCellValue("</jx:forEach>");

        i ++;
    }

    // 輸出
    workbook.write(out);
    out.close();
    return tempFile;
}

  • Excel模版生成樣式如下圖

<jx:forEach items = "${data1}" var="data"> 中"data1"可以隨意命名,但需要與自己定義的EXCEL_DEFAULT_SHEET_NAME值一致,多個sheet頁的時候,就從data1,data2,data3...遞增開始就可以了,這樣可以方便統一管理。多個sheet頁原理一樣,只不過要保證每個sheet頁的循環便利的數據源命名要不一樣。

配置完模版后,sheet頁數據渲染方法實現

  • 使用的工具類

    • XLSTransformer.transformXLS 方法解析

/**

     * 

     * @param srcFilePath Excel模版文件路徑

     * @param beanParams Map<String, Object>類型參數,key為模版中值對象名

     * @param destFilePath 導出文件路徑

     */

public void transformXLS(String srcFilePath, Map beanParams, String destFilePath) throws ParsePropertyException, IOException, InvalidFormatException {

        InputStream is = new BufferedInputStream(new FileInputStream(srcFilePath));

        Workbook workbook = this.transformXLS(is, beanParams);

        OutputStream os = new BufferedOutputStream(new FileOutputStream(destFilePath));

        workbook.write(os);

        is.close();

        os.flush();

        os.close();

    }

  • 代碼實現

/**
 * 導出單個sheet頁excel文件
 * @param exportPath
 * @param columnList
 * @param columnValueList
 */
public static File export(String exportPath, List<ExcelColumnVO> columnList, List<Map<String, Object>> columnValueList) {
    Map<String, Object> paramMap = new HashMap<String, Object>();
    paramMap.put(EXCEL_DEFAULT_DATA, columnValueList);
    XLSTransformer transformer = new XLSTransformer();

    try {
        String tempPath = getTempFilePath();
        File tempFile = createSingleSheetTempExcel(tempPath, columnList);
        transformer.transformXLS(tempPath, paramMap, exportPath);

        tempFile.delete();
    } catch (ParsePropertyException e) {
        LOGGER.error(e.getMessage());
    } catch (IOException e) {
        LOGGER.error(e.getMessage());
    } catch (InvalidFormatException e) {
        LOGGER.error(e.getMessage());
    }
    return new File(exportPath);
}

/**
 * 創建臨時excel單個sheet頁模版文件
 * @param tempPath 模版預存路徑
 * @param columnList excel列名數組
 * @return
 * @throws IOException
 */
public static File createSingleSheetTempExcel(String tempPath, List<ExcelColumnVO> columnList) throws IOException {
    Map<String, List<ExcelColumnVO>> columnMoreSheetMap = new HashMap<>();
    columnMoreSheetMap.put(EXCEL_DEFAULT_SHEET_NAME, columnList);
    return createMoreSheetTempExcel(tempPath, columnMoreSheetMap);
}

/**
 * 導出多個sheet頁excel文件
 */
public static File exportMoreSheet(String exportPath, Map<String, List<ExcelColumnVO>> columnMoreSheetMap, Map<String, List<Map<String, Object>>> columnValueMap) {
    XLSTransformer transformer = new XLSTransformer();
    File exportFile = new File(exportPath);
    try {
        String tempPath = getTempFilePath();
        File tempFile = createMoreSheetTempExcel(tempPath, columnMoreSheetMap);
        Workbook hssfWorkbook = WorkbookFactory.create(new FileInputStream(tempFile));
        transformer.transformWorkbook(hssfWorkbook, columnValueMap);
        FileOutputStream fileOut = new FileOutputStream(exportFile);

        hssfWorkbook.write(fileOut);
        fileOut.close();

        tempFile.delete();
    } catch (ParsePropertyException e) {
        LOGGER.error(e.getMessage());
    } catch (IOException e) {
        LOGGER.error(e.getMessage());
    } catch (InvalidFormatException e) {
        LOGGER.error(e.getMessage());
    }
    return exportFile;
}


/**
 * 創建臨時excel多個sheet頁模版文件
 * @param tempPath 模版路徑
 * @param columnMoreSheetMap 每個sheet頁列名屬性,每個sheet頁map key值需指定為data1,data2,data3...
 * @return
 * @throws IOException
 */
public static File createMoreSheetTempExcel(String tempPath, Map<String, List<ExcelColumnVO>> columnMoreSheetMap) throws IOException {
    XSSFWorkbook workbook = new XSSFWorkbook();
    // 創建一個目錄和文件名
    File tempFile = new File(tempPath);
    FileOutputStream out = new FileOutputStream(tempFile);
    Integer i = 1;
    for(Map.Entry<String, List<ExcelColumnVO>> entry : columnMoreSheetMap.entrySet()){
        String sheetName = entry.getKey();
        List<ExcelColumnVO> columnList = entry.getValue();
        // 創建一個工作表
        XSSFSheet spreadsheet = workbook.createSheet(sheetName);
        // 創建一行
        XSSFRow firstRow = spreadsheet.createRow(0);
        XSSFRow twoRow = spreadsheet.createRow(2);
        AtomicInteger firstCellid = new AtomicInteger();
        AtomicInteger secondCellid = new AtomicInteger();

        //設置模版行字段
        List<ExcelColumnVO> columnSortList = columnList.stream()
                .sorted(Comparator.comparing(ExcelColumnVO :: getSeq))
                .collect(Collectors.toList());
        int headerCellCount = 0;
        for (ExcelColumnVO columnVO : columnSortList) {
            String columnDesc = columnVO.getColumnDesc();
            spreadsheet.setColumnWidth(firstCellid.getAndIncrement(), columnDesc.getBytes().length * 2 * 256);
            Cell firstCell = firstRow.createCell(headerCellCount);
            firstCell.setCellValue(columnDesc);
            Cell secondCell = twoRow.createCell(secondCellid.getAndIncrement());
            secondCell.setCellValue("${data." + columnVO.getColumnCode() + "}");
            headerCellCount ++;
        }

        XSSFCellStyle style6 = workbook.createCellStyle();
        style6.setAlignment(HorizontalAlignment.CENTER);
        XSSFRow row1 = spreadsheet.createRow(1);
        Cell cell = row1.createCell(0);
        cell.setCellValue("<jx:forEach items = \"${data" + i + "}\" var=\"data\">");
        cell.setCellStyle(style6);
        CellRangeAddress region = new CellRangeAddress(1, 1, 0, firstCellid.intValue());
        spreadsheet.addMergedRegion(region);

        XSSFRow thirdRow = spreadsheet.createRow(3);
        Cell thirdCell = thirdRow.createCell(0);
        thirdCell.setCellValue("</jx:forEach>");

        i ++;
    }

    // 輸出
    workbook.write(out);
    out.close();
    return tempFile;
}

/**
 * 生成一個隨機文件路徑
 * @return
 */
public static String getTempFilePath() {
    String tmpDir = System.getProperty("java.io.tmpdir");
    return tmpDir + UUID.randomUUID().toString().replace("-", "") + ".xlsx";
}

記錄-多個sheet頁源碼解析

  • XLSTransformer類中提供了transformMultipleSheetsList方法,但在具體調用時,發現每個sheet頁的參數實現的並不是友好,源碼如下

    • beanParams在接受參數時,key值限制的很死,字符串拼接,動態效果不好,所以我直接沒使用這個方法,因為在這個方法里我看到了
      transformWorkbook(hssfWorkbook, (Map)beanParams),我不用它自帶的for循環獲取參數值,我自己重新封裝
    • 改寫后的方法如下,自己重新分裝每個sheet頁的數據,transformer.transformWorkbook(hssfWorkbook, columnValueMap)渲染,最后寫入指定目錄的文件,搞定
public static File exportMoreSheet(String exportPath, Map<String, List<ExcelColumnVO>> columnMoreSheetMap, Map<String, List<Map<String, Object>>> columnValueMap) {
    XLSTransformer transformer = new XLSTransformer();
    File exportFile = new File(exportPath);
    try {
        String tempPath = getTempFilePath();
        File tempFile = createMoreSheetTempExcel(tempPath, columnMoreSheetMap);
        Workbook hssfWorkbook = WorkbookFactory.create(new FileInputStream(tempFile));
        transformer.transformWorkbook(hssfWorkbook, columnValueMap);
        FileOutputStream fileOut = new FileOutputStream(exportFile);

        hssfWorkbook.write(fileOut);
        fileOut.close();

        tempFile.delete();
    } catch (ParsePropertyException e) {
        LOGGER.error(e.getMessage());
    } catch (IOException e) {
        LOGGER.error(e.getMessage());
    } catch (InvalidFormatException e) {
        LOGGER.error(e.getMessage());
    }
    return exportFile;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM