最近項目上,需要使用動態數據源來生成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循環獲取參數值,我自己重新封裝
- beanParams在接受參數時,key值限制的很死,字符串拼接,動態效果不好,所以我直接沒使用這個方法,因為在這個方法里我看到了
-
- 改寫后的方法如下,自己重新分裝每個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;
}