diff --git a/img/readme/quickstart/write/dynamicHeadWrite.png b/img/readme/quickstart/write/dynamicHeadWrite.png new file mode 100644 index 00000000..3b25ad72 Binary files /dev/null and b/img/readme/quickstart/write/dynamicHeadWrite.png differ diff --git a/quickstart.md b/quickstart.md index 030a80a5..4b2d4ac1 100644 --- a/quickstart.md +++ b/quickstart.md @@ -21,6 +21,7 @@ DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/ja * [自定义样式](#styleWrite) * [合并单元格](#mergeWrite) * [使用table去写入](#tableWrite) +* [动态头,实时生成头写入](#dynamicHeadWrite) * [web中的写](#webWrite) ## 读excel样例 @@ -697,6 +698,47 @@ public class WidthAndHeightData { } ``` +### 动态头,实时生成头写入 +##### excel示例 +![img](img/readme/quickstart/write/dynamicHeadWrite.png) +##### 对象 +参照:[对象](#simpleWriteObject) +##### 代码 +```java + /** + * 动态头,实时生成头写入 + *

+ * 思路是这样子的,先创建List头格式的sheet仅仅写入头,然后通过table 不写入头的方式 去写入数据 + * + *

  • 1. 创建excel对应的实体对象 参照{@link DemoData} + *
  • 2. 然后写入table即可 + */ + @Test + public void dynamicHeadWrite() { + String fileName = TestFileUtil.getPath() + "dynamicHeadWrite" + System.currentTimeMillis() + ".xlsx"; + // write的时候 不传入 class 在table的时候传入 + EasyExcelFactory.write(fileName) + // 这里放入动态头 + .head(head()).sheet("模板") + // table的时候 传入class 并且设置needHead =false + .table().head(DemoData.class).needHead(Boolean.FALSE).doWrite(data()); + } + + private List> head() { + List> list = new ArrayList>(); + List head0 = new ArrayList(); + head0.add("字符串" + System.currentTimeMillis()); + List head1 = new ArrayList(); + head1.add("数字" + System.currentTimeMillis()); + List head2 = new ArrayList(); + head2.add("日期" + System.currentTimeMillis()); + list.add(head0); + list.add(head1); + list.add(head2); + return list; + } +``` + ### web中的写 ##### 示例代码 DEMO代码地址:[https://github.com/alibaba/easyexcel/blob/master/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java](/src/test/java/com/alibaba/easyexcel/test/demo/web/WebTest.java) diff --git a/src/main/java/com/alibaba/excel/annotation/write/style/ColumnWidth.java b/src/main/java/com/alibaba/excel/annotation/write/style/ColumnWidth.java index 47ccbeeb..ae07ff00 100644 --- a/src/main/java/com/alibaba/excel/annotation/write/style/ColumnWidth.java +++ b/src/main/java/com/alibaba/excel/annotation/write/style/ColumnWidth.java @@ -15,10 +15,11 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface ColumnWidth { + /** * Column width *

    - * -1 mean the auto set width + * -1 means the default column width is used */ int value() default -1; } diff --git a/src/main/java/com/alibaba/excel/context/WriteContextImpl.java b/src/main/java/com/alibaba/excel/context/WriteContextImpl.java index e4dbec82..18331305 100644 --- a/src/main/java/com/alibaba/excel/context/WriteContextImpl.java +++ b/src/main/java/com/alibaba/excel/context/WriteContextImpl.java @@ -275,7 +275,7 @@ public class WriteContextImpl implements WriteContext { } for (WriteHandler writeHandler : handlerList) { if (writeHandler instanceof CellWriteHandler) { - ((CellWriteHandler)writeHandler).afterCellCreate(writeSheetHolder, writeTableHolder, cell, head, + ((CellWriteHandler)writeHandler).afterCellCreate(writeSheetHolder, writeTableHolder, null, cell, head, relativeRowIndex, true); } } diff --git a/src/main/java/com/alibaba/excel/metadata/AbstractHolder.java b/src/main/java/com/alibaba/excel/metadata/AbstractHolder.java index e6e03784..1694e5e4 100644 --- a/src/main/java/com/alibaba/excel/metadata/AbstractHolder.java +++ b/src/main/java/com/alibaba/excel/metadata/AbstractHolder.java @@ -36,12 +36,12 @@ public abstract class AbstractHolder implements ConfigurationHolder { public AbstractHolder(BasicParameter basicParameter, AbstractHolder prentAbstractHolder) { this.newInitialization = Boolean.TRUE; - if (basicParameter.getHead() == null && prentAbstractHolder != null) { + if (basicParameter.getHead() == null && basicParameter.getClazz() == null && prentAbstractHolder != null) { this.head = prentAbstractHolder.getHead(); } else { this.head = basicParameter.getHead(); } - if (basicParameter.getClazz() == null && prentAbstractHolder != null) { + if (basicParameter.getHead() == null && basicParameter.getClazz() == null && prentAbstractHolder != null) { this.clazz = prentAbstractHolder.getClazz(); } else { this.clazz = basicParameter.getClazz(); diff --git a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java index a15ed79d..25dee195 100644 --- a/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java +++ b/src/main/java/com/alibaba/excel/write/ExcelBuilderImpl.java @@ -179,8 +179,8 @@ public class ExcelBuilderImpl implements ExcelBuilder { beforeCellCreate(row, head, relativeRowIndex); Cell cell = WorkBookUtil.createCell(row, cellIndex); Object value = oneRowData.get(dataIndex); - converterAndSet(context.currentWriteHolder(), value.getClass(), cell, value, null); - afterCellCreate(head, cell, relativeRowIndex); + CellData cellData = converterAndSet(context.currentWriteHolder(), value.getClass(), cell, value, null); + afterCellCreate(head, cellData, cell, relativeRowIndex); } private void addJavaObjectToExcel(Object oneRowData, Row row, int relativeRowIndex, List fieldList) { @@ -202,9 +202,9 @@ public class ExcelBuilderImpl implements ExcelBuilder { beforeCellCreate(row, head, relativeRowIndex); Cell cell = WorkBookUtil.createCell(row, cellIndex); Object value = beanMap.get(name); - converterAndSet(currentWriteHolder, excelContentProperty.getField().getType(), cell, value, - excelContentProperty); - afterCellCreate(head, cell, relativeRowIndex); + CellData cellData = converterAndSet(currentWriteHolder, excelContentProperty.getField().getType(), cell, + value, excelContentProperty); + afterCellCreate(head, cellData, cell, relativeRowIndex); beanMapHandledSet.add(name); } // Finish @@ -229,8 +229,8 @@ public class ExcelBuilderImpl implements ExcelBuilder { } beforeCellCreate(row, null, relativeRowIndex); Cell cell = WorkBookUtil.createCell(row, cellIndex++); - converterAndSet(currentWriteHolder, value.getClass(), cell, value, null); - afterCellCreate(null, cell, relativeRowIndex); + CellData cellData = converterAndSet(currentWriteHolder, value.getClass(), cell, value, null); + afterCellCreate(null, cellData, cell, relativeRowIndex); } } @@ -261,7 +261,7 @@ public class ExcelBuilderImpl implements ExcelBuilder { } - private void afterCellCreate(Head head, Cell cell, int relativeRowIndex) { + private void afterCellCreate(Head head, CellData cellData, Cell cell, int relativeRowIndex) { List handlerList = context.currentWriteHolder().writeHandlerMap().get(CellWriteHandler.class); if (handlerList == null || handlerList.isEmpty()) { return; @@ -269,7 +269,7 @@ public class ExcelBuilderImpl implements ExcelBuilder { for (WriteHandler writeHandler : handlerList) { if (writeHandler instanceof CellWriteHandler) { ((CellWriteHandler)writeHandler).afterCellCreate(context.writeSheetHolder(), context.writeTableHolder(), - cell, head, relativeRowIndex, false); + cellData, cell, head, relativeRowIndex, false); } } if (null != context.writeWorkbookHolder().getWriteWorkbook().getWriteHandler()) { @@ -277,10 +277,10 @@ public class ExcelBuilderImpl implements ExcelBuilder { } } - private void converterAndSet(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, + private CellData converterAndSet(WriteHolder currentWriteHolder, Class clazz, Cell cell, Object value, ExcelContentProperty excelContentProperty) { if (value == null) { - return; + return null; } if (value instanceof String && currentWriteHolder.globalConfiguration().getAutoTrim()) { value = ((String)value).trim(); @@ -296,13 +296,13 @@ public class ExcelBuilderImpl implements ExcelBuilder { switch (cellData.getType()) { case STRING: cell.setCellValue(cellData.getStringValue()); - return; + return cellData; case BOOLEAN: cell.setCellValue(cellData.getBooleanValue()); - return; + return cellData; case NUMBER: cell.setCellValue(cellData.getDoubleValue()); - return; + return cellData; default: throw new ExcelDataConvertException("Not supported data:" + value + " return type:" + cell.getCellType() + "at row:" + cell.getRow().getRowNum()); diff --git a/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java b/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java index 38e6f407..9e8af22b 100644 --- a/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java +++ b/src/main/java/com/alibaba/excel/write/handler/CellWriteHandler.java @@ -3,6 +3,7 @@ package com.alibaba.excel.write.handler; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; +import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; import com.alibaba.excel.write.metadata.holder.WriteTableHolder; @@ -36,9 +37,11 @@ public interface CellWriteHandler extends WriteHandler { * Nullable * @param cell * @param head + * @param cellData + * Nullable. * @param relativeRowIndex * @param isHead */ - void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, - int relativeRowIndex, boolean isHead); + void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, + Cell cell, Head head, int relativeRowIndex, boolean isHead); } diff --git a/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java b/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java index 9cb71f6e..09513562 100644 --- a/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java +++ b/src/main/java/com/alibaba/excel/write/merge/AbstractMergeStrategy.java @@ -4,6 +4,7 @@ import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; +import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.write.handler.CellWriteHandler; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; @@ -22,8 +23,8 @@ public abstract class AbstractMergeStrategy implements CellWriteHandler { } @Override - public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, - Head head, int relativeRowIndex, boolean isHead) { + public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, Cell cell, + Head head, int relativeRowIndex, boolean isHead) { if (isHead) { return; } diff --git a/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java index 6adb4089..51b03d13 100644 --- a/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/AbstractCellStyleStrategy.java @@ -5,6 +5,7 @@ import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Workbook; import com.alibaba.excel.event.NotRepeatExecutor; +import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.write.handler.CellWriteHandler; import com.alibaba.excel.write.handler.SheetWriteHandler; @@ -46,8 +47,8 @@ public abstract class AbstractCellStyleStrategy implements CellWriteHandler, She } @Override - public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, - Head head, int relativeRowIndex, boolean isHead) { + public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, + Cell cell, Head head, int relativeRowIndex, boolean isHead) { if (isHead) { setHeadCellStyle(cell, head, relativeRowIndex); } else { diff --git a/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java index 4d2d0afe..67e9733a 100644 --- a/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/column/AbstractColumnWidthStyleStrategy.java @@ -2,9 +2,9 @@ package com.alibaba.excel.write.style.column; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.Row; -import org.apache.poi.ss.usermodel.Sheet; import com.alibaba.excel.event.NotRepeatExecutor; +import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; import com.alibaba.excel.write.handler.CellWriteHandler; import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; @@ -29,20 +29,22 @@ public abstract class AbstractColumnWidthStyleStrategy implements CellWriteHandl } @Override - public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, - Head head, int relativeRowIndex, boolean isHead) { - setColumnWidth(writeSheetHolder.getSheet(), cell, head, relativeRowIndex, isHead); + public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, CellData cellData, + Cell cell, Head head, int relativeRowIndex, boolean isHead) { + setColumnWidth(writeSheetHolder, cellData, cell, head, relativeRowIndex, isHead); } /** * Sets the column width when head create * - * @param sheet + * @param writeSheetHolder + * @param cellData * @param cell * @param head * @param relativeRowIndex * @param isHead */ - protected abstract void setColumnWidth(Sheet sheet, Cell cell, Head head, int relativeRowIndex, boolean isHead); + protected abstract void setColumnWidth(WriteSheetHolder writeSheetHolder, CellData cellData, Cell cell, Head head, + int relativeRowIndex, boolean isHead); } diff --git a/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java index 59050430..802f15f2 100644 --- a/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java +++ b/src/main/java/com/alibaba/excel/write/style/column/AbstractHeadColumnWidthStyleStrategy.java @@ -1,9 +1,10 @@ package com.alibaba.excel.write.style.column; import org.apache.poi.ss.usermodel.Cell; -import org.apache.poi.ss.usermodel.Sheet; +import com.alibaba.excel.metadata.CellData; import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; /** * Returns the column width according to each column header @@ -12,14 +13,15 @@ import com.alibaba.excel.metadata.Head; */ public abstract class AbstractHeadColumnWidthStyleStrategy extends AbstractColumnWidthStyleStrategy { @Override - protected void setColumnWidth(Sheet sheet, Cell cell, Head head, int relativeRowIndex, boolean isHead) { + protected void setColumnWidth(WriteSheetHolder writeSheetHolder, CellData cellData, Cell cell, Head head, + int relativeRowIndex, boolean isHead) { if (!isHead && relativeRowIndex != 0) { return; } Integer width = columnWidth(head); if (width != null) { width = width * 256; - sheet.setColumnWidth(cell.getColumnIndex(), width); + writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), width); } } diff --git a/src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java b/src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java new file mode 100644 index 00000000..5c6ec8c9 --- /dev/null +++ b/src/main/java/com/alibaba/excel/write/style/column/LongestMatchColumnWidthStyleStrategy.java @@ -0,0 +1,66 @@ +package com.alibaba.excel.write.style.column; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.poi.ss.usermodel.Cell; + +import com.alibaba.excel.metadata.CellData; +import com.alibaba.excel.metadata.Head; +import com.alibaba.excel.write.metadata.holder.WriteSheetHolder; + +/** + * Take the width of the longest column as the width. + *

    + * This is not very useful at the moment, for example if you have Numbers it will cause a newline.And the length is not + * exactly the same as the actual length. + * + * @author Jiaju Zhuang + */ +public class LongestMatchColumnWidthStyleStrategy extends AbstractColumnWidthStyleStrategy { + + private static final int MAX_COLUMN_WIDTH = 256; + + Map> cache = new HashMap>(8); + + @Override + protected void setColumnWidth(WriteSheetHolder writeSheetHolder, CellData cellData, Cell cell, Head head, + int relativeRowIndex, boolean isHead) { + if (!isHead && cellData == null) { + return; + } + Map maxColumnWidthMap = cache.get(writeSheetHolder.getSheetNo()); + if (maxColumnWidthMap == null) { + maxColumnWidthMap = new HashMap(16); + cache.put(writeSheetHolder.getSheetNo(), maxColumnWidthMap); + } + Integer columnWidth = dataLength(cellData, cell, isHead); + if (columnWidth < 0) { + return; + } + if (columnWidth > MAX_COLUMN_WIDTH) { + columnWidth = MAX_COLUMN_WIDTH; + } + Integer maxColumnWidth = maxColumnWidthMap.get(head.getColumnIndex()); + if (maxColumnWidth == null || columnWidth > maxColumnWidth) { + maxColumnWidthMap.put(head.getColumnIndex(), columnWidth); + writeSheetHolder.getSheet().setColumnWidth(head.getColumnIndex(), columnWidth * 256); + } + } + + private Integer dataLength(CellData cellData, Cell cell, boolean isHead) { + if (isHead) { + return cell.getStringCellValue().getBytes().length; + } + switch (cellData.getType()) { + case STRING: + return cellData.getStringValue().getBytes().length; + case BOOLEAN: + return cellData.getBooleanValue().toString().getBytes().length; + case NUMBER: + return cellData.getDoubleValue().toString().getBytes().length; + default: + return -1; + } + } +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/write/LongestMatchColumnWidthData.java b/src/test/java/com/alibaba/easyexcel/test/demo/write/LongestMatchColumnWidthData.java new file mode 100644 index 00000000..69ad51dc --- /dev/null +++ b/src/test/java/com/alibaba/easyexcel/test/demo/write/LongestMatchColumnWidthData.java @@ -0,0 +1,22 @@ +package com.alibaba.easyexcel.test.demo.write; + +import java.util.Date; + +import com.alibaba.excel.annotation.ExcelProperty; + +import lombok.Data; + +/** + * 基础数据类 + * + * @author Jiaju Zhuang + **/ +@Data +public class LongestMatchColumnWidthData { + @ExcelProperty("字符串标题") + private String string; + @ExcelProperty("日期标题很长日期标题很长日期标题很长很长") + private Date date; + @ExcelProperty("数字") + private Double doubleData; +} diff --git a/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java b/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java index 0888b36c..d105fb76 100644 --- a/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java +++ b/src/test/java/com/alibaba/easyexcel/test/demo/write/WriteTest.java @@ -25,6 +25,7 @@ import com.alibaba.excel.write.metadata.WriteTable; import com.alibaba.excel.write.metadata.style.WriteCellStyle; import com.alibaba.excel.write.metadata.style.WriteFont; import com.alibaba.excel.write.style.HorizontalCellStyleStrategy; +import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy; /** * 写的常见写法 @@ -218,6 +219,70 @@ public class WriteTest { excelWriter.finish(); } + /** + * 动态头,实时生成头写入 + *

    + * 思路是这样子的,先创建List头格式的sheet仅仅写入头,然后通过table 不写入头的方式 去写入数据 + * + *

  • 1. 创建excel对应的实体对象 参照{@link DemoData} + *
  • 2. 然后写入table即可 + */ + @Test + public void dynamicHeadWrite() { + String fileName = TestFileUtil.getPath() + "dynamicHeadWrite" + System.currentTimeMillis() + ".xlsx"; + // write的时候 不传入 class 在table的时候传入 + EasyExcelFactory.write(fileName) + // 这里放入动态头 + .head(head()).sheet("模板") + // table的时候 传入class 并且设置needHead =false + .table().head(DemoData.class).needHead(Boolean.FALSE).doWrite(data()); + } + + /** + * 自动列宽(不太精确) + *

    + * 这个目前不是很好用,比如有数字就会导致换行。而且长度也不是刚好和实际长度一致。 所以需要精确到刚好列宽的慎用。 当然也可以自己参照 + * {@link LongestMatchColumnWidthStyleStrategy}重新实现. + * + *

  • 1. 创建excel对应的实体对象 参照{@link DemoData} + *
  • 3. 注册策略{@link LongestMatchColumnWidthStyleStrategy} + *
  • 2. 直接写即可 + */ + @Test + public void longestMatchColumnWidthWrite() { + String fileName = + TestFileUtil.getPath() + "longestMatchColumnWidthWrite" + System.currentTimeMillis() + ".xlsx"; + // 这里 需要指定写用哪个class去读,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 + EasyExcelFactory.write(fileName, LongestMatchColumnWidthData.class) + .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).sheet("模板").doWrite(dataLong()); + } + + private List dataLong() { + List list = new ArrayList(); + for (int i = 0; i < 10; i++) { + LongestMatchColumnWidthData data = new LongestMatchColumnWidthData(); + data.setString("测试很长的字符串测试很长的字符串测试很长的字符串" + i); + data.setDate(new Date()); + data.setDoubleData(1000000000000.0); + list.add(data); + } + return list; + } + + private List> head() { + List> list = new ArrayList>(); + List head0 = new ArrayList(); + head0.add("字符串" + System.currentTimeMillis()); + List head1 = new ArrayList(); + head1.add("数字" + System.currentTimeMillis()); + List head2 = new ArrayList(); + head2.add("日期" + System.currentTimeMillis()); + list.add(head0); + list.add(head1); + list.add(head2); + return list; + } + private List data() { List list = new ArrayList(); for (int i = 0; i < 10; i++) {