一種操作複雜樣式Excel的思路
基於上一篇 提到的想法,今天抽了時間實現出來了。
先貼截圖。

執行結果
個人覺得填充底色,合併單元格,各種設定格式之流的操作用純程式碼來實現還是蠻耗費精力的。但是,如果是基於下面這樣已經調好格式的模板,只需要往裡填充資料的話,工作量要小很多。

模板
模板通過2個sheet拼接完成。程式碼填充好第一個sheet的資料之後,再把第二個sheet的內容拷貝到第一個sheet的最後。
資料是通過POJO和自定義註解配合完成的。目前註解主要用於獲得單元格所對應的行,後期還會新增其他屬性。時間關係,POJO的屬性暫時也只支援了String一種型別。
註解定義如下:
/** * @author nathan * @date 2019/3/24 */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface ExcelProperties { int index(); }
一個示例POJO如下:
/** * @author nathan * @date 2019/3/24 */ @Getter @Setter public class FooDTO { @ExcelProperties(index = 0) private String empty1; @ExcelProperties(index = 1) private String busiName; @ExcelProperties(index = 2) private String busiType; @ExcelProperties(index = 3) private String empty2; @ExcelProperties(index = 4) private String value1; @ExcelProperties(index = 5) private String value2; @ExcelProperties(index = 6) private String empty3; }
一個可以執行的Demo:
/** * @author nathan * @date 2019/3/24 */ public class RptUtil { public static void main(String[] args) { // 從第1行,第4列開始,使用者指定 int startRow = 0; int startCol = 4; List<FooDTO> foos = getFooList(); int sheet2StartRow = 1; int sheet2StartCol = 2; File template = new File("/tmp/template.xlsx"); File output = new File("/tmp/test.xlsx"); // 從template檔案讀取2個sheet // sheet1從第0行,第4列開始插入資料 // sheet2從第1行,第2列開始讀取 write(template, output, foos, startRow, startCol, true, sheet2StartRow, sheet2StartCol); } private static List<FooDTO> getFooList() { FooDTO foo1 = new FooDTO(); foo1.setBusiName("測試名稱1"); foo1.setBusiType("測試型別1"); foo1.setValue1("50.12"); foo1.setValue2("12.86"); return Lists.newArrayList(foo1); } public static <T> void write(File template, File output, List<T> data, int startRow, int startCol, boolean copySheet2, int sheet2StartRow, int sheet2StartCol) { FileInputStream in = null; FileOutputStream out = null; try { in = new FileInputStream(template); XSSFWorkbook source = new XSSFWorkbook(in); // 獲得sheet1的內容 XSSFSheet sheet1 = source.getSheetAt(0); // 插入主體資料1 insertData(data, sheet1, startRow, startCol); if (copySheet2) { copyFromSheet2(source, sheet1, startCol + data.size() - sheet2StartCol, sheet2StartRow, sheet2StartCol); } // 寫入到output out = new FileOutputStream(output); source.write(out); } catch (Exception e) { e.printStackTrace(); } finally { close(in); close(out); } } /** * 關閉資源 * * @param closeable */ private static void close(Closeable closeable) { if(closeable != null) { try { closeable.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 插入資料(List形式) * * @param data * @param sheet * @param startRow * @param startCol * @param <T> * @throws Exception */ private static <T> void insertData(List<T> data, XSSFSheet sheet, int startRow, int startCol) throws Exception { for (T t : data) { insertOne(t, sheet, startRow, startCol); startCol++; } } /** * 插入資料(單條) * * @param data * @param sheet * @param startRow * @param startCol * @param <T> * @throws Exception */ private static <T> void insertOne(T data, XSSFSheet sheet, int startRow, int startCol) throws Exception { XSSFCell cell; XSSFRow row; for (Field field : data.getClass().getDeclaredFields()) { int index = -1; String value = ""; if (field.isAnnotationPresent(ExcelProperties.class)) { ExcelProperties props = field.getAnnotation(ExcelProperties.class); field.setAccessible(true); if (field.getGenericType().toString().equals("class java.lang.String")) { String name = field.getName(); name = name.substring(0, 1).toUpperCase() + name.substring(1); Method m = data.getClass().getMethod("get" + name); String val = (String) m.invoke(data); index = props.index(); value = val; } } if (index != -1) { row = sheet.getRow(startRow + index); // 找到最後一個cell Cell lastCell = row.getCell(row.getLastCellNum() - 1); // 建立一個cell cell = row.createCell(startCol); // 拷貝樣式 cell.setCellStyle(lastCell.getCellStyle()); // 設定值 cell.setCellValue(value); } } } /** * 把sheet2的內容拷貝到sheet1 * * @param source * @param sheet * @param startCol * @param sheet2StartRow * @param sheet2StartCol */ private static void copyFromSheet2(XSSFWorkbook source, XSSFSheet sheet, int startCol, int sheet2StartRow, int sheet2StartCol) { // 從sheet2獲得剩餘部分內容,並插入到sheet1 XSSFCell cell; XSSFRow row; // 插入sheet2的內容 XSSFSheet sheet2 = source.getSheetAt(1); Iterator<Row> iterator = sheet2.iterator(); // 跳過若干行 while (sheet2StartRow > 1) { iterator.next(); sheet2StartRow--; } int count = 0; while (iterator.hasNext()) { XSSFRow current = (XSSFRow) iterator.next(); row = sheet.getRow(count); for (int j = sheet2StartCol; j < current.getLastCellNum(); j++) { cell = row.createCell(startCol + j); cell.copyCellFrom(current.getCell(j), new CellCopyPolicy()); } count++; } // 刪除sheet2 source.removeSheetAt(1); } }
maven依賴:
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.0.1</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</artifactId> <version>3.17</version> </dependency>