diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/map/BeanMap.java b/hutool-core/src/main/java/org/dromara/hutool/core/map/BeanMap.java index 603a737ac..c5ebd282c 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/map/BeanMap.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/map/BeanMap.java @@ -88,6 +88,16 @@ public class BeanMap implements Map { return null; } + /** + * 获取Path表达式对应的值 + * + * @param expression Path表达式 + * @return 值 + */ + public Object getProperty(final String expression) { + return BeanUtil.getProperty(bean, expression); + } + @Override public Object put(final String key, final Object value) { final PropDesc propDesc = this.propDescMap.get(key); @@ -99,6 +109,16 @@ public class BeanMap implements Map { return null; } + /** + * 设置Path表达式对应的值 + * + * @param expression Path表达式 + * @param value 新值 + */ + public void putProperty(final String expression, final Object value) { + BeanUtil.setProperty(bean, expression, value); + } + @Override public Object remove(final Object key) { throw new UnsupportedOperationException("Can not remove field for Bean!"); diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/meta/HttpHeaderUtil.java b/hutool-http/src/main/java/org/dromara/hutool/http/meta/HttpHeaderUtil.java index 27f062fdb..5c950e588 100644 --- a/hutool-http/src/main/java/org/dromara/hutool/http/meta/HttpHeaderUtil.java +++ b/hutool-http/src/main/java/org/dromara/hutool/http/meta/HttpHeaderUtil.java @@ -19,6 +19,7 @@ package org.dromara.hutool.http.meta; import org.dromara.hutool.core.collection.CollUtil; import org.dromara.hutool.core.map.CaseInsensitiveMap; import org.dromara.hutool.core.net.url.UrlDecoder; +import org.dromara.hutool.core.net.url.UrlEncoder; import org.dromara.hutool.core.regex.ReUtil; import org.dromara.hutool.core.text.StrUtil; import org.dromara.hutool.core.text.split.SplitUtil; @@ -55,6 +56,22 @@ public class HttpHeaderUtil { return headersIgnoreCase.get(name.trim()); } + /** + * 生成Content-Disposition头,用于下载文件
+ * 格式为: + *
{@code
+	 *     attachment;filename="example.txt";filename*=UTF-8''example.txt
+	 * }
+ * + * @param fileName 文件名 + * @param charset 编码 + * @return Content-Disposition头 + */ + public static String createAttachmentDisposition(final String fileName, final Charset charset) { + final String encodeText = UrlEncoder.encodeAll(fileName, charset); + return StrUtil.format("attachment;filename=\"{}\";filename*={}''{}", encodeText, charset.name(), encodeText); + } + /** * 从Content-Disposition头中获取文件名。
* 参考标准:https://datatracker.ietf.org/doc/html/rfc6266#section-4.1
diff --git a/hutool-http/src/main/java/org/dromara/hutool/http/server/servlet/ServletUtil.java b/hutool-http/src/main/java/org/dromara/hutool/http/server/servlet/ServletUtil.java index 4adaa34c7..a4275c5a1 100644 --- a/hutool-http/src/main/java/org/dromara/hutool/http/server/servlet/ServletUtil.java +++ b/hutool-http/src/main/java/org/dromara/hutool/http/server/servlet/ServletUtil.java @@ -16,52 +16,38 @@ package org.dromara.hutool.http.server.servlet; +import jakarta.servlet.ServletOutputStream; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.http.Cookie; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.dromara.hutool.core.array.ArrayUtil; import org.dromara.hutool.core.bean.BeanUtil; import org.dromara.hutool.core.bean.copier.CopyOptions; import org.dromara.hutool.core.bean.copier.ValueProvider; import org.dromara.hutool.core.collection.ListUtil; import org.dromara.hutool.core.collection.iter.ArrayIter; import org.dromara.hutool.core.exception.HutoolException; -import org.dromara.hutool.core.io.file.FileUtil; import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.io.IoUtil; +import org.dromara.hutool.core.io.file.FileUtil; import org.dromara.hutool.core.map.CaseInsensitiveMap; import org.dromara.hutool.core.map.MapUtil; import org.dromara.hutool.core.net.NetUtil; -import org.dromara.hutool.core.net.url.UrlEncoder; -import org.dromara.hutool.http.multipart.MultipartFormData; -import org.dromara.hutool.http.multipart.UploadSetting; import org.dromara.hutool.core.reflect.ConstructorUtil; import org.dromara.hutool.core.text.StrUtil; -import org.dromara.hutool.core.array.ArrayUtil; import org.dromara.hutool.core.util.CharsetUtil; import org.dromara.hutool.core.util.ObjUtil; - +import org.dromara.hutool.http.meta.HeaderName; +import org.dromara.hutool.http.meta.HttpHeaderUtil; import org.dromara.hutool.http.meta.Method; -import jakarta.servlet.ServletOutputStream; -import jakarta.servlet.ServletRequest; -import jakarta.servlet.http.Cookie; -import jakarta.servlet.http.HttpServletRequest; -import jakarta.servlet.http.HttpServletResponse; +import org.dromara.hutool.http.multipart.MultipartFormData; +import org.dromara.hutool.http.multipart.UploadSetting; -import java.io.BufferedInputStream; -import java.io.BufferedReader; -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintWriter; -import java.io.Writer; +import java.io.*; import java.lang.reflect.Type; import java.nio.charset.Charset; -import java.util.Collection; -import java.util.Collections; -import java.util.Date; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * Servlet相关工具类封装 @@ -610,10 +596,8 @@ public class ServletUtil { */ public static void write(final HttpServletResponse response, final InputStream in, final String contentType, final String fileName) { final String charset = ObjUtil.defaultIfNull(response.getCharacterEncoding(), CharsetUtil.NAME_UTF_8); - final String encodeText = UrlEncoder.encodeAll(fileName, CharsetUtil.charset(charset)); - response.setHeader("Content-Disposition", - StrUtil.format("attachment;filename=\"{}\";filename*={}''{}", encodeText, charset, encodeText)); response.setContentType(contentType); + response.setHeader(HeaderName.CONTENT_DISPOSITION.getValue(), HttpHeaderUtil.createAttachmentDisposition(fileName, CharsetUtil.charset(charset))); write(response, in); } diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelBase.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelBase.java index 427ff1122..313e796da 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelBase.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/ExcelBase.java @@ -22,18 +22,13 @@ import org.apache.poi.ss.util.CellReference; import org.apache.poi.xssf.streaming.SXSSFSheet; import org.apache.poi.xssf.usermodel.XSSFSheet; import org.apache.poi.xssf.usermodel.XSSFWorkbook; -import org.dromara.hutool.core.data.id.IdUtil; import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.core.lang.Assert; -import org.dromara.hutool.core.net.url.UrlEncoder; -import org.dromara.hutool.core.text.StrUtil; -import org.dromara.hutool.core.util.CharsetUtil; import org.dromara.hutool.poi.excel.cell.CellUtil; import org.dromara.hutool.poi.excel.style.StyleUtil; import java.io.Closeable; import java.io.File; -import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; @@ -110,16 +105,7 @@ public class ExcelBase, C extends ExcelConfig> impleme return this.workbook; } - /** - * 创建字体 - * - * @return 字体 - * @since 4.1.0 - */ - public Font createFont() { - return getWorkbook().createFont(); - } - + // region ----- sheet ops /** * 返回工作簿表格数 * @@ -246,7 +232,9 @@ public class ExcelBase, C extends ExcelConfig> impleme } return (T) this; } + // endregion + // region ----- cell ops /** * 获取指定坐标单元格,单元格不存在时返回{@code null} * @@ -320,8 +308,9 @@ public class ExcelBase, C extends ExcelConfig> impleme public Cell getCell(final int x, final int y, final boolean isCreateIfNotExist) { return CellUtil.getCell(this.sheet, x, y, isCreateIfNotExist); } + // endregion - + // region ----- row ops /** * 获取或者创建行 * @@ -333,6 +322,36 @@ public class ExcelBase, C extends ExcelConfig> impleme return RowUtil.getOrCreateRow(this.sheet, y); } + /** + * 获取总行数,计算方法为: + * + *
+	 * 最后一行序号 + 1
+	 * 
+ * + * @return 行数 + * @since 4.5.4 + */ + public int getRowCount() { + return this.sheet.getLastRowNum() + 1; + } + + /** + * 获取有记录的行数,计算方法为: + * + *
+	 * 最后一行序号 - 第一行序号 + 1
+	 * 
+ * + * @return 行数 + * @since 4.5.4 + */ + public int getPhysicalRowCount() { + return this.sheet.getPhysicalNumberOfRows(); + } + // endregion + + // region ----- style ops /** * 为指定单元格获取或者创建样式,返回样式后可以设置样式内容 * @@ -448,6 +467,18 @@ public class ExcelBase, C extends ExcelConfig> impleme return columnStyle; } + /** + * 创建字体 + * + * @return 字体 + * @since 4.1.0 + */ + public Font createFont() { + return getWorkbook().createFont(); + } + // endregion + + // region ----- hyperlink ops /** * 创建 {@link Hyperlink},默认内容(标签为链接地址本身) * @@ -475,34 +506,7 @@ public class ExcelBase, C extends ExcelConfig> impleme hyperlink.setLabel(label); return hyperlink; } - - /** - * 获取总行数,计算方法为: - * - *
-	 * 最后一行序号 + 1
-	 * 
- * - * @return 行数 - * @since 4.5.4 - */ - public int getRowCount() { - return this.sheet.getLastRowNum() + 1; - } - - /** - * 获取有记录的行数,计算方法为: - * - *
-	 * 最后一行序号 - 第一行序号 + 1
-	 * 
- * - * @return 行数 - * @since 4.5.4 - */ - public int getPhysicalRowCount() { - return this.sheet.getPhysicalNumberOfRows(); - } + // endregion /** * 获取第一行总列数,计算方法为: @@ -560,31 +564,6 @@ public class ExcelBase, C extends ExcelConfig> impleme return isXlsx() ? ExcelUtil.XLSX_CONTENT_TYPE : ExcelUtil.XLS_CONTENT_TYPE; } - /** - * 获取Content-Disposition头对应的值,可以通过调用以下方法快速设置下载Excel的头信息: - * - *
-	 * response.setHeader("Content-Disposition", excelWriter.getDisposition("test.xlsx", CharsetUtil.CHARSET_UTF_8));
-	 * 
- * - * @param fileName 文件名,如果文件名没有扩展名,会自动按照生成Excel类型补齐扩展名,如果提供空,使用随机UUID - * @param charset 编码,null则使用默认UTF-8编码 - * @return Content-Disposition值 - */ - public String getDisposition(String fileName, Charset charset) { - if (null == charset) { - charset = CharsetUtil.UTF_8; - } - - if (StrUtil.isBlank(fileName)) { - // 未提供文件名使用随机UUID作为文件名 - fileName = IdUtil.fastSimpleUUID(); - } - - fileName = StrUtil.addSuffixIfNot(UrlEncoder.encodeAll(fileName, charset), isXlsx() ? ".xlsx" : ".xls"); - return StrUtil.format("attachment; filename=\"{}\"", fileName); - } - /** * 关闭工作簿
* 如果用户设定了目标文件,先写出目标文件后给关闭工作簿 @@ -596,4 +575,11 @@ public class ExcelBase, C extends ExcelConfig> impleme this.workbook = null; this.isClosed = true; } + + /** + * 校验Excel是否已经关闭 + */ + protected void checkClosed() { + Assert.isFalse(this.isClosed, "Excel has been closed!"); + } } diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/SheetUtil.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/SheetUtil.java index 84c9c4586..153b91619 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/SheetUtil.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/SheetUtil.java @@ -50,11 +50,7 @@ public class SheetUtil { Sheet sheet; if (null == sheetName) { - sheet = book.getSheetAt(0); - if (null == sheet) { - // 工作簿中无sheet,创建默认 - sheet = book.createSheet(); - } + sheet = getOrCreateSheet(book, 0); } else { sheet = book.getSheet(sheetName); if (null == sheet) { diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/ExcelReader.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/ExcelReader.java index ddf7dba1e..37798f54c 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/ExcelReader.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/reader/ExcelReader.java @@ -233,7 +233,7 @@ public class ExcelReader extends ExcelBase { * @since 5.3.8 */ public void read(final int startRowIndex, final int endRowIndex, final SerBiConsumer cellHandler) { - checkNotClosed(); + checkClosed(); final WalkSheetReader reader = new WalkSheetReader(startRowIndex, endRowIndex, cellHandler); reader.setExcelConfig(this.config); @@ -315,7 +315,7 @@ public class ExcelReader extends ExcelBase { * @since 5.4.4 */ public T read(final SheetReader sheetReader) { - checkNotClosed(); + checkClosed(); return Assert.notNull(sheetReader).read(this.sheet); } @@ -395,13 +395,6 @@ public class ExcelReader extends ExcelBase { return RowUtil.readRow(row, this.config.getCellEditor()); } - /** - * 检查是否未关闭状态 - */ - private void checkNotClosed() { - Assert.isFalse(this.isClosed, "ExcelReader has been closed!"); - } - /** * 获取Sheet,如果不存在则关闭{@link Workbook}并抛出异常,解决当sheet不存在时,文件依旧被占用问题
* 见:Issue#I8ZIQC diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/ExcelWriter.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/ExcelWriter.java index da7d3af92..9ac8eb36a 100644 --- a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/ExcelWriter.java +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/ExcelWriter.java @@ -16,21 +16,16 @@ package org.dromara.hutool.poi.excel.writer; -import org.apache.poi.common.usermodel.Hyperlink; import org.apache.poi.ss.usermodel.*; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.CellRangeAddressList; import org.apache.poi.ss.util.CellReference; import org.dromara.hutool.core.bean.BeanUtil; -import org.dromara.hutool.core.collection.ListUtil; import org.dromara.hutool.core.io.IORuntimeException; import org.dromara.hutool.core.io.IoUtil; import org.dromara.hutool.core.io.file.FileUtil; import org.dromara.hutool.core.lang.Assert; -import org.dromara.hutool.core.map.MapUtil; -import org.dromara.hutool.core.map.concurrent.SafeConcurrentHashMap; -import org.dromara.hutool.core.map.multi.Table; -import org.dromara.hutool.core.text.StrUtil; +import org.dromara.hutool.poi.POIException; import org.dromara.hutool.poi.excel.*; import org.dromara.hutool.poi.excel.cell.CellRangeUtil; import org.dromara.hutool.poi.excel.cell.CellUtil; @@ -41,8 +36,10 @@ import java.awt.Color; import java.io.File; import java.io.IOException; import java.io.OutputStream; -import java.util.*; -import java.util.concurrent.atomic.AtomicInteger; +import java.util.Comparator; +import java.util.Iterator; +import java.util.Map; +import java.util.TreeMap; /** * Excel 写入器
@@ -62,18 +59,8 @@ public class ExcelWriter extends ExcelBase { * 样式集,定义不同类型数据样式 */ private StyleSet styleSet; - /** - * 标题项对应列号缓存,每次写标题更新此缓存 - */ - private Map headLocationCache; - /** - * 当前行,用于标记初始可写数据的行和部分写完后当前的行 - */ - private final AtomicInteger currentRow; - /** - * 模板上下文,存储模板中变量及其位置信息 - */ - private TemplateContext templateContext; + private SheetDataWriter sheetDataWriter; + private SheetTemplateWriter sheetTemplateWriter; // region ----- Constructors @@ -155,7 +142,7 @@ public class ExcelWriter extends ExcelBase { } else { // 如果是已经存在的文件,则作为模板加载,此时不能写出到模板文件 // 初始化模板 - this.templateContext = new TemplateContext(this.sheet); + this.sheetTemplateWriter = new SheetTemplateWriter(this.sheet, this.config); } } @@ -182,7 +169,6 @@ public class ExcelWriter extends ExcelBase { public ExcelWriter(final Sheet sheet) { super(new ExcelWriteConfig(), sheet); this.styleSet = new DefaultStyleSet(workbook); - this.currentRow = new AtomicInteger(0); } // endregion @@ -192,20 +178,6 @@ public class ExcelWriter extends ExcelBase { return super.setConfig(config); } - @Override - public ExcelWriter setSheet(final int sheetIndex) { - super.setSheet(sheetIndex); - // 切换到新sheet需要重置开始行 - return reset(); - } - - @Override - public ExcelWriter setSheet(final String sheetName) { - super.setSheet(sheetName); - // 切换到新sheet需要重置开始行 - return reset(); - } - /** * 重置Writer,包括: * @@ -217,8 +189,47 @@ public class ExcelWriter extends ExcelBase { * @return this */ public ExcelWriter reset() { - this.headLocationCache.clear(); - return resetRow(); + this.sheetDataWriter = null; + return this; + } + + /** + * 关闭工作簿
+ * 如果用户设定了目标文件,先写出目标文件后给关闭工作簿 + */ + @SuppressWarnings("resource") + @Override + public void close() { + if (null != this.targetFile) { + flush(); + } + closeWithoutFlush(); + } + + /** + * 关闭工作簿但是不写出 + */ + protected void closeWithoutFlush() { + super.close(); + this.reset(); + + // 清空样式 + this.styleSet = null; + } + + // region ----- sheet ops + @Override + public ExcelWriter setSheet(final int sheetIndex) { + super.setSheet(sheetIndex); + // 切换到新sheet需要重置开始行 + return reset(); + } + + @Override + public ExcelWriter setSheet(final String sheetName) { + super.setSheet(sheetName); + // 切换到新sheet需要重置开始行 + return reset(); } /** @@ -244,6 +255,7 @@ public class ExcelWriter extends ExcelBase { this.workbook.setSheetName(sheet, sheetName); return this; } + // endregion /** * 设置所有列为自动宽度,不考虑合并单元格
@@ -303,6 +315,9 @@ public class ExcelWriter extends ExcelBase { */ public ExcelWriter setStyleSet(final StyleSet styleSet) { this.styleSet = styleSet; + if (null != this.sheetDataWriter) { + this.sheetDataWriter.setStyleSet(styleSet); + } return this; } @@ -329,7 +344,7 @@ public class ExcelWriter extends ExcelBase { * @return 当前行 */ public int getCurrentRow() { - return this.currentRow.get(); + return null == this.sheetDataWriter ? 0 : this.sheetDataWriter.getCurrentRow(); } /** @@ -339,7 +354,7 @@ public class ExcelWriter extends ExcelBase { * @return this */ public ExcelWriter setCurrentRow(final int rowIndex) { - this.currentRow.set(rowIndex); + getSheetDataWriter().setCurrentRow(rowIndex); return this; } @@ -359,18 +374,18 @@ public class ExcelWriter extends ExcelBase { * @return this */ public ExcelWriter passCurrentRow() { - this.currentRow.incrementAndGet(); + getSheetDataWriter().passAndGet(); return this; } /** * 跳过指定行数 * - * @param rows 跳过的行数 + * @param rowNum 跳过的行数 * @return this */ - public ExcelWriter passRows(final int rows) { - this.currentRow.addAndGet(rows); + public ExcelWriter passRows(final int rowNum) { + getSheetDataWriter().passRowsAndGet(rowNum); return this; } @@ -380,7 +395,7 @@ public class ExcelWriter extends ExcelBase { * @return this */ public ExcelWriter resetRow() { - this.currentRow.set(0); + getSheetDataWriter().resetRow(); return this; } @@ -584,14 +599,14 @@ public class ExcelWriter extends ExcelBase { */ @SuppressWarnings("resource") public ExcelWriter merge(final int lastColumn, final Object content, final boolean isSetHeaderStyle) { - Assert.isFalse(this.isClosed, "ExcelWriter has been closed!"); + checkClosed(); - final int rowIndex = this.currentRow.get(); + final int rowIndex = getCurrentRow(); merge(CellRangeUtil.ofSingleRow(rowIndex, lastColumn), content, isSetHeaderStyle); // 设置内容后跳到下一行 if (null != content) { - this.currentRow.incrementAndGet(); + this.passCurrentRow(); } return this; } @@ -606,7 +621,7 @@ public class ExcelWriter extends ExcelBase { * @since 4.0.10 */ public ExcelWriter merge(final CellRangeAddress cellRangeAddress, final Object content, final boolean isSetHeaderStyle) { - Assert.isFalse(this.isClosed, "ExcelWriter has been closed!"); + checkClosed(); CellStyle style = null; if (null != this.styleSet) { @@ -627,7 +642,7 @@ public class ExcelWriter extends ExcelBase { * @since 5.6.5 */ public ExcelWriter merge(final CellRangeAddress cellRangeAddress, final Object content, final CellStyle cellStyle) { - Assert.isFalse(this.isClosed, "ExcelWriter has been closed!"); + checkClosed(); CellUtil.mergingCells(this.getSheet(), cellRangeAddress, cellStyle); @@ -684,7 +699,7 @@ public class ExcelWriter extends ExcelBase { */ @SuppressWarnings("resource") public ExcelWriter write(final Iterable data, final boolean isWriteKeyAsHead) { - Assert.isFalse(this.isClosed, "ExcelWriter has been closed!"); + checkClosed(); boolean isFirst = true; for (final Object object : data) { writeRow(object, isFirst && isWriteKeyAsHead); @@ -712,7 +727,7 @@ public class ExcelWriter extends ExcelBase { */ @SuppressWarnings({"rawtypes", "unchecked", "resource"}) public ExcelWriter write(final Iterable data, final Comparator comparator) { - Assert.isFalse(this.isClosed, "ExcelWriter has been closed!"); + checkClosed(); boolean isFirstRow = true; Map map; for (final Object obj : data) { @@ -845,21 +860,8 @@ public class ExcelWriter extends ExcelBase { * @return this */ public ExcelWriter writeHeadRow(final Iterable rowData) { - Assert.isFalse(this.isClosed, "ExcelWriter has been closed!"); - this.headLocationCache = new SafeConcurrentHashMap<>(); - - final int rowNum = this.currentRow.getAndIncrement(); - final Row row = this.config.insertRow ? this.sheet.createRow(rowNum) : RowUtil.getOrCreateRow(this.sheet, rowNum); - - final CellEditor cellEditor = this.config.getCellEditor(); - int i = 0; - Cell cell; - for (final Object value : rowData) { - cell = CellUtil.getOrCreateCell(row, i); - CellUtil.setCellValue(cell, value, this.styleSet, true, cellEditor); - this.headLocationCache.put(StrUtil.toString(value), i); - i++; - } + checkClosed(); + getSheetDataWriter().writeHeadRow(rowData); return this; } @@ -877,12 +879,15 @@ public class ExcelWriter extends ExcelBase { */ @SuppressWarnings("resource") public ExcelWriter writeSecHeadRow(final Iterable rowData) { - final Row row = RowUtil.getOrCreateRow(this.sheet, this.currentRow.getAndIncrement()); + checkClosed(); + final Row row = getOrCreateRow(getCurrentRow()); + passCurrentRow(); + final Iterator iterator = rowData.iterator(); //如果获取的row存在单元格,则执行复杂表头逻辑,否则直接调用writeHeadRow(Iterable rowData) if (row.getLastCellNum() != 0) { final CellEditor cellEditor = this.config.getCellEditor(); - for (int i = 0; i < this.workbook.getSpreadsheetVersion().getMaxColumns(); i++) { + for (int i = 0; ; i++) { Cell cell = row.getCell(i); if (cell != null) { continue; @@ -916,46 +921,29 @@ public class ExcelWriter extends ExcelBase { * @see #writeRow(Map, boolean) * @since 4.1.5 */ - @SuppressWarnings({"rawtypes", "unchecked"}) public ExcelWriter writeRow(final Object rowBean, final boolean isWriteKeyAsHead) { - final ExcelWriteConfig config = this.config; + checkClosed(); - final Map rowMap; - if (rowBean instanceof Map) { - if (MapUtil.isNotEmpty(config.getHeaderAlias())) { - rowMap = MapUtil.newTreeMap((Map) rowBean, config.getCachedAliasComparator()); - } else { - rowMap = (Map) rowBean; - } - } else if (rowBean instanceof Iterable) { - // issue#2398@Github - // MapWrapper由于实现了Iterable接口,应该优先按照Map处理 - return writeRow((Iterable) rowBean); - } else if (rowBean instanceof Hyperlink) { - // Hyperlink当成一个值 - return writeRow(ListUtil.of(rowBean), isWriteKeyAsHead); - } else if (BeanUtil.isReadableBean(rowBean.getClass())) { - if (MapUtil.isEmpty(config.getHeaderAlias())) { - rowMap = BeanUtil.beanToMap(rowBean, new LinkedHashMap<>(), false, false); - } else { - // 别名存在情况下按照别名的添加顺序排序Bean数据 - rowMap = BeanUtil.beanToMap(rowBean, new TreeMap<>(config.getCachedAliasComparator()), false, false); - } - } else { - // 其它转为字符串默认输出 - return writeRow(ListUtil.of(rowBean), isWriteKeyAsHead); + // 模板写出 + if (null != this.sheetTemplateWriter) { + this.sheetTemplateWriter.fillRow(rowBean); + return this; } - return writeRow(rowMap, isWriteKeyAsHead); + + getSheetDataWriter().writeRow(rowBean, isWriteKeyAsHead); + return this; } /** * 填充非列表模板变量(一次性变量) * - * @param rowMap 行数据 + * @param rowMap 行数据 * @return this */ public ExcelWriter fillOnce(final Map rowMap) { - rowMap.forEach((key, value) -> this.templateContext.fill(StrUtil.toStringOrNull(key), rowMap, false)); + checkClosed(); + Assert.notNull(this.sheetTemplateWriter, () -> new POIException("No template for this writer!")); + this.sheetTemplateWriter.fillOnce(rowMap); return this; } @@ -967,51 +955,16 @@ public class ExcelWriter extends ExcelBase { * @param isWriteKeyAsHead 为true写出两行,Map的keys做为一行,values做为第二行,否则只写出一行values * @return this */ - @SuppressWarnings("resource") public ExcelWriter writeRow(final Map rowMap, final boolean isWriteKeyAsHead) { - Assert.isFalse(this.isClosed, "ExcelWriter has been closed!"); - if (MapUtil.isEmpty(rowMap)) { - // 如果写出数据为null或空,跳过当前行 - return passCurrentRow(); - } + checkClosed(); // 模板写出 - if (null != this.templateContext) { - fillRow(rowMap, this.config.insertRow); + if (null != this.sheetTemplateWriter) { + this.sheetTemplateWriter.fillRow(rowMap); return this; } - final Table aliasTable = this.config.aliasTable(rowMap); - if (isWriteKeyAsHead) { - // 写出标题行,并记录标题别名和列号的关系 - writeHeadRow(aliasTable.columnKeys()); - // 记录原数据key和别名对应列号 - int i = 0; - for (final Object key : aliasTable.rowKeySet()) { - this.headLocationCache.putIfAbsent(StrUtil.toString(key), i); - i++; - } - } - - // 如果已经写出标题行,根据标题行找对应的值写入 - if (MapUtil.isNotEmpty(this.headLocationCache)) { - final Row row = RowUtil.getOrCreateRow(this.sheet, this.currentRow.getAndIncrement()); - final CellEditor cellEditor = this.config.getCellEditor(); - Integer location; - for (final Table.Cell cell : aliasTable) { - // 首先查找原名对应的列号 - location = this.headLocationCache.get(StrUtil.toString(cell.getRowKey())); - if (null == location) { - // 未找到,则查找别名对应的列号 - location = this.headLocationCache.get(StrUtil.toString(cell.getColumnKey())); - } - if (null != location) { - CellUtil.setCellValue(CellUtil.getOrCreateCell(row, location), cell.getValue(), this.styleSet, false, cellEditor); - } - } - } else { - writeRow(aliasTable.values()); - } + getSheetDataWriter().writeRow(rowMap, isWriteKeyAsHead); return this; } @@ -1024,11 +977,8 @@ public class ExcelWriter extends ExcelBase { * @return this */ public ExcelWriter writeRow(final Iterable rowData) { - Assert.isFalse(this.isClosed, "ExcelWriter has been closed!"); - - final int rowNum = this.currentRow.getAndIncrement(); - final Row row = this.config.insertRow ? this.sheet.createRow(rowNum) : RowUtil.getOrCreateRow(this.sheet, rowNum); - RowUtil.writeRow(row, rowData, this.styleSet, false, this.config.getCellEditor()); + checkClosed(); + getSheetDataWriter().writeRow(rowData); return this; } // endregion @@ -1098,8 +1048,8 @@ public class ExcelWriter extends ExcelBase { */ @SuppressWarnings("resource") public ExcelWriter writeCol(final Object headerVal, final int colIndex, final Iterable colData, final boolean isResetRowIndex) { - Assert.isFalse(this.isClosed, "ExcelWriter has been closed!"); - int currentRowIndex = currentRow.get(); + checkClosed(); + int currentRowIndex = getCurrentRow(); if (null != headerVal) { writeCellValue(colIndex, currentRowIndex, headerVal, true); currentRowIndex++; @@ -1109,7 +1059,7 @@ public class ExcelWriter extends ExcelBase { currentRowIndex++; } if (!isResetRowIndex) { - currentRow.set(currentRowIndex); + setCurrentRow(currentRowIndex); } return this; } @@ -1344,7 +1294,7 @@ public class ExcelWriter extends ExcelBase { * @since 4.4.1 */ public ExcelWriter flush(final OutputStream out, final boolean isCloseOut) throws IORuntimeException { - Assert.isFalse(this.isClosed, "ExcelWriter has been closed!"); + checkClosed(); try { this.workbook.write(out); @@ -1361,53 +1311,14 @@ public class ExcelWriter extends ExcelBase { // endregion /** - * 关闭工作簿
- * 如果用户设定了目标文件,先写出目标文件后给关闭工作簿 - */ - @SuppressWarnings("resource") - @Override - public void close() { - if (null != this.targetFile) { - flush(); - } - closeWithoutFlush(); - } - - /** - * 关闭工作簿但是不写出 - */ - protected void closeWithoutFlush() { - super.close(); - this.currentRow.set(0); - - // 清空对象 - this.styleSet = null; - } - - /** - * 填充模板行,用于列表填充 + * 获取SheetDataWriter,没有则创建 * - * @param rowMap 行数据 - * @param insertRow 是否插入行,如果为{@code true},则已有行下移,否则利用已有行 + * @return SheetDataWriter */ - private void fillRow(final Map rowMap, final boolean insertRow) { - if(insertRow){ - // 当前填充行的模板行以下全部下移 - final int bottomRowIndex = this.templateContext.getBottomRowIndex(rowMap); - if(bottomRowIndex < 0){ - // 无可填充行 - return; - } - if(bottomRowIndex != 0){ - final int lastRowNum = this.sheet.getLastRowNum(); - if(bottomRowIndex <= lastRowNum){ - // 填充行底部需有数据,无数据跳过 - // 虚拟行的行号就是需要填充的行,这行的已有数据整体下移 - this.sheet.shiftRows(bottomRowIndex, this.sheet.getLastRowNum(), 1); - } - } + private SheetDataWriter getSheetDataWriter() { + if (null == this.sheetDataWriter) { + this.sheetDataWriter = new SheetDataWriter(this.sheet, this.config, this.styleSet); } - - rowMap.forEach((key, value) -> this.templateContext.fill(StrUtil.toStringOrNull(key), rowMap, true)); + return this.sheetDataWriter; } } diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/SheetDataWriter.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/SheetDataWriter.java new file mode 100644 index 000000000..7be7792dc --- /dev/null +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/SheetDataWriter.java @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2024 Hutool Team and hutool.cn + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dromara.hutool.poi.excel.writer; + +import org.apache.poi.common.usermodel.Hyperlink; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.usermodel.Row; +import org.apache.poi.ss.usermodel.Sheet; +import org.dromara.hutool.core.bean.BeanUtil; +import org.dromara.hutool.core.collection.ListUtil; +import org.dromara.hutool.core.map.MapUtil; +import org.dromara.hutool.core.map.concurrent.SafeConcurrentHashMap; +import org.dromara.hutool.core.map.multi.Table; +import org.dromara.hutool.core.text.StrUtil; +import org.dromara.hutool.poi.excel.RowUtil; +import org.dromara.hutool.poi.excel.cell.CellUtil; +import org.dromara.hutool.poi.excel.cell.editors.CellEditor; +import org.dromara.hutool.poi.excel.style.StyleSet; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.TreeMap; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Sheet数据写出器
+ * 此对象只封装将数据写出到Sheet中,并不刷新到文件 + * + * @author looly + * @since 6.0.0 + */ +public class SheetDataWriter { + + private final Sheet sheet; + private final ExcelWriteConfig config; + private StyleSet styleSet; + + /** + * 标题项对应列号缓存,每次写标题更新此缓存
+ * 此缓存用于用户多次write时,寻找标题位置 + */ + private Map headLocationCache; + /** + * 当前行,用于标记初始可写数据的行和部分写完后当前的行 + */ + private final AtomicInteger currentRow; + + /** + * 构造 + * + * @param sheet {@link Sheet} + * @param config Excel配置 + * @param styleSet 样式表 + */ + public SheetDataWriter(final Sheet sheet, final ExcelWriteConfig config, final StyleSet styleSet) { + this.sheet = sheet; + this.config = config; + this.styleSet = styleSet; + this.currentRow = new AtomicInteger(0); + } + + /** + * 设置样式表 + * @param styleSet 样式表 + * @return this + */ + public SheetDataWriter setStyleSet(final StyleSet styleSet) { + this.styleSet = styleSet; + return this; + } + + /** + * 写出一行,根据rowBean数据类型不同,写出情况如下: + * + *
+	 * 1、如果为Iterable,直接写出一行
+	 * 2、如果为Map,isWriteKeyAsHead为true写出两行,Map的keys做为一行,values做为第二行,否则只写出一行values
+	 * 3、如果为Bean,转为Map写出,isWriteKeyAsHead为true写出两行,Map的keys做为一行,values做为第二行,否则只写出一行values
+	 * 
+ * + * @param rowBean 写出的Bean + * @param isWriteKeyAsHead 为true写出两行,Map的keys做为一行,values做为第二行,否则只写出一行values + * @return this + * @see #writeRow(Iterable) + * @see #writeRow(Map, boolean) + * @since 4.1.5 + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public SheetDataWriter writeRow(final Object rowBean, final boolean isWriteKeyAsHead) { + final ExcelWriteConfig config = this.config; + + final Map rowMap; + if (rowBean instanceof Map) { + if (MapUtil.isNotEmpty(config.getHeaderAlias())) { + rowMap = MapUtil.newTreeMap((Map) rowBean, config.getCachedAliasComparator()); + } else { + rowMap = (Map) rowBean; + } + } else if (rowBean instanceof Iterable) { + // issue#2398@Github + // MapWrapper由于实现了Iterable接口,应该优先按照Map处理 + return writeRow((Iterable) rowBean); + } else if (rowBean instanceof Hyperlink) { + // Hyperlink当成一个值 + return writeRow(ListUtil.of(rowBean), isWriteKeyAsHead); + } else if (BeanUtil.isReadableBean(rowBean.getClass())) { + if (MapUtil.isEmpty(config.getHeaderAlias())) { + rowMap = BeanUtil.beanToMap(rowBean, new LinkedHashMap<>(), false, false); + } else { + // 别名存在情况下按照别名的添加顺序排序Bean数据 + rowMap = BeanUtil.beanToMap(rowBean, new TreeMap<>(config.getCachedAliasComparator()), false, false); + } + } else { + // 其它转为字符串默认输出 + return writeRow(ListUtil.of(rowBean), isWriteKeyAsHead); + } + return writeRow(rowMap, isWriteKeyAsHead); + } + + /** + * 将一个Map写入到Excel,isWriteKeyAsHead为true写出两行,Map的keys做为一行,values做为第二行,否则只写出一行values
+ * 如果rowMap为空(包括null),则写出空行 + * + * @param rowMap 写出的Map,为空(包括null),则写出空行 + * @param isWriteKeyAsHead 为true写出两行,Map的keys做为一行,values做为第二行,否则只写出一行values + * @return this + */ + public SheetDataWriter writeRow(final Map rowMap, final boolean isWriteKeyAsHead) { + if (MapUtil.isEmpty(rowMap)) { + // 如果写出数据为null或空,跳过当前行 + passAndGet(); + return this; + } + + final Table aliasTable = this.config.aliasTable(rowMap); + if (isWriteKeyAsHead) { + // 写出标题行,并记录标题别名和列号的关系 + writeHeadRow(aliasTable.columnKeys()); + // 记录原数据key和别名对应列号 + int i = 0; + for (final Object key : aliasTable.rowKeySet()) { + this.headLocationCache.putIfAbsent(StrUtil.toString(key), i); + i++; + } + } + + // 如果已经写出标题行,根据标题行找对应的值写入 + if (MapUtil.isNotEmpty(this.headLocationCache)) { + final Row row = RowUtil.getOrCreateRow(this.sheet, this.currentRow.getAndIncrement()); + final CellEditor cellEditor = this.config.getCellEditor(); + Integer columnIndex; + for (final Table.Cell cell : aliasTable) { + columnIndex = getColumnIndex(cell); + if (null != columnIndex) { + CellUtil.setCellValue(CellUtil.getOrCreateCell(row, columnIndex), cell.getValue(), this.styleSet, false, cellEditor); + } + } + } else { + writeRow(aliasTable.values()); + } + return this; + } + + /** + * 写出一行标题数据
+ * 本方法只是将数据写入Workbook中的Sheet,并不写出到文件
+ * 写出的起始行为当前行号,可使用{@link #getCurrentRow()}方法调用,根据写出的的行数,当前行号自动+1 + * + * @param rowData 一行的数据 + * @return this + */ + public SheetDataWriter writeHeadRow(final Iterable rowData) { + this.headLocationCache = new SafeConcurrentHashMap<>(); + + final int rowNum = this.currentRow.getAndIncrement(); + final Row row = this.config.insertRow ? this.sheet.createRow(rowNum) : RowUtil.getOrCreateRow(this.sheet, rowNum); + + final CellEditor cellEditor = this.config.getCellEditor(); + int i = 0; + Cell cell; + for (final Object value : rowData) { + cell = CellUtil.getOrCreateCell(row, i); + CellUtil.setCellValue(cell, value, this.styleSet, true, cellEditor); + this.headLocationCache.put(StrUtil.toString(value), i); + i++; + } + return this; + } + + /** + * 写出一行数据
+ * 本方法只是将数据写入Workbook中的Sheet,并不写出到文件
+ * 写出的起始行为当前行号,可使用{@link #getCurrentRow()}方法调用,根据写出的的行数,当前行号自动+1 + * + * @param rowData 一行的数据 + * @return this + */ + public SheetDataWriter writeRow(final Iterable rowData) { + final int rowNum = this.currentRow.getAndIncrement(); + final Row row = this.config.insertRow ? this.sheet.createRow(rowNum) : RowUtil.getOrCreateRow(this.sheet, rowNum); + RowUtil.writeRow(row, rowData, this.styleSet, false, this.config.getCellEditor()); + return this; + } + + // region ----- currentRow ops + /** + * 获得当前行 + * + * @return 当前行 + */ + public int getCurrentRow() { + return this.currentRow.get(); + } + + /** + * 设置当前所在行 + * + * @param rowIndex 行号 + * @return this + */ + public SheetDataWriter setCurrentRow(final int rowIndex) { + this.currentRow.set(rowIndex); + return this; + } + + /** + * 跳过当前行,并获取下一行的行号 + * + * @return this + */ + public int passAndGet() { + return this.currentRow.incrementAndGet(); + } + + /** + * 跳过指定行数,并获取当前行号 + * + * @param rowNum 跳过的行数 + * @return this + */ + public int passRowsAndGet(final int rowNum) { + return this.currentRow.addAndGet(rowNum); + } + + /** + * 重置当前行为0 + * + * @return this + */ + public SheetDataWriter resetRow() { + this.currentRow.set(0); + return this; + } + // endregion + + /** + * 查找标题或标题别名对应的列号 + * + * @param cell 别名表,rowKey:原名,columnKey:别名 + * @return 列号,如果未找到返回null + */ + private Integer getColumnIndex(final Table.Cell cell) { + // 首先查找原名对应的列号 + Integer location = this.headLocationCache.get(StrUtil.toString(cell.getRowKey())); + if (null == location) { + // 未找到,则查找别名对应的列号 + location = this.headLocationCache.get(StrUtil.toString(cell.getColumnKey())); + } + return location; + } +} diff --git a/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/SheetTemplateWriter.java b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/SheetTemplateWriter.java new file mode 100644 index 000000000..ad9059957 --- /dev/null +++ b/hutool-poi/src/main/java/org/dromara/hutool/poi/excel/writer/SheetTemplateWriter.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024 Hutool Team and hutool.cn + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.dromara.hutool.poi.excel.writer; + +import org.apache.poi.ss.usermodel.Sheet; +import org.dromara.hutool.core.map.BeanMap; +import org.dromara.hutool.core.text.StrUtil; + +import java.util.Map; + +/** + * 模板Excel写入器
+ * 解析已有模板,并填充模板中的变量为数据 + * + * @author Looly + * @since 6.0.0 + */ +public class SheetTemplateWriter { + + private final Sheet sheet; + private final ExcelWriteConfig config; + /** + * 模板上下文,存储模板中变量及其位置信息 + */ + private final TemplateContext templateContext; + + /** + * 构造 + * + * @param sheet {@link Sheet} + * @param config Excel写配置 + */ + public SheetTemplateWriter(final Sheet sheet, final ExcelWriteConfig config) { + this.sheet = sheet; + this.config = config; + this.templateContext = new TemplateContext(sheet); + } + + /** + * 填充非列表模板变量(一次性变量) + * + * @param rowMap 行数据 + * @return this + */ + public SheetTemplateWriter fillOnce(final Map rowMap) { + rowMap.forEach((key, value) -> this.templateContext.fill(StrUtil.toStringOrNull(key), rowMap, false)); + return this; + } + + /** + * 填充模板行,用于列表填充 + * + * @param rowBean 行的Bean数据 + * @return this + */ + public SheetTemplateWriter fillRow(final Object rowBean) { + // TODO 支持Bean的级联属性获取 + return fillRow(new BeanMap(rowBean)); + } + + /** + * 填充模板行,用于列表填充 + * + * @param rowMap 行数据 + * @return this + */ + public SheetTemplateWriter fillRow(final Map rowMap) { + if (this.config.insertRow) { + // 当前填充行的模板行以下全部下移 + final int bottomRowIndex = this.templateContext.getBottomRowIndex(rowMap); + if (bottomRowIndex < 0) { + // 无可填充行 + return this; + } + if (bottomRowIndex != 0) { + final int lastRowNum = this.sheet.getLastRowNum(); + if (bottomRowIndex <= lastRowNum) { + // 填充行底部需有数据,无数据跳过 + // 虚拟行的行号就是需要填充的行,这行的已有数据整体下移 + this.sheet.shiftRows(bottomRowIndex, this.sheet.getLastRowNum(), 1); + } + } + } + + rowMap.forEach((key, value) -> this.templateContext.fill(StrUtil.toStringOrNull(key), rowMap, true)); + + return this; + } +} diff --git a/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/ExcelWriteTest.java b/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/ExcelWriteTest.java index 3667c6ade..b54c2762d 100644 --- a/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/ExcelWriteTest.java +++ b/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/ExcelWriteTest.java @@ -23,14 +23,14 @@ import org.dromara.hutool.core.date.DateUtil; import org.dromara.hutool.core.io.file.FileUtil; import org.dromara.hutool.core.lang.Console; import org.dromara.hutool.core.map.MapUtil; -import org.dromara.hutool.core.util.CharsetUtil; import org.dromara.hutool.core.util.ObjUtil; -import org.dromara.hutool.poi.excel.*; +import org.dromara.hutool.poi.excel.ExcelUtil; +import org.dromara.hutool.poi.excel.OrderExcel; +import org.dromara.hutool.poi.excel.TestBean; import org.dromara.hutool.poi.excel.cell.setters.EscapeStrCellSetter; import org.dromara.hutool.poi.excel.reader.ExcelReader; import org.dromara.hutool.poi.excel.style.DefaultStyleSet; import org.dromara.hutool.poi.excel.style.StyleUtil; -import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; @@ -873,13 +873,6 @@ public class ExcelWriteTest { writer.close(); } - @Test - public void getDispositionTest() { - final ExcelWriter writer = ExcelUtil.getWriter(true); - final String disposition = writer.getDisposition("测试A12.xlsx", CharsetUtil.UTF_8); - Assertions.assertEquals("attachment; filename=\"%E6%B5%8B%E8%AF%95A12.xlsx\"", disposition); - } - @Test @Disabled public void autoSizeColumnTest() { diff --git a/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/TemplateWriterTest.java b/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/TemplateWriterTest.java index f924a22cd..3d4a0f2c0 100644 --- a/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/TemplateWriterTest.java +++ b/hutool-poi/src/test/java/org/dromara/hutool/poi/excel/writer/TemplateWriterTest.java @@ -1,6 +1,5 @@ package org.dromara.hutool.poi.excel.writer; -import org.dromara.hutool.core.io.file.FileUtil; import org.dromara.hutool.core.map.MapUtil; import org.dromara.hutool.poi.excel.ExcelUtil; import org.junit.jupiter.api.Test; @@ -29,7 +28,7 @@ public class TemplateWriterTest { writer.writeRow(createRow(), false); } - writer.flush(FileUtil.file(targetDir + "templateResult.xlsx"), true); + //writer.flush(FileUtil.file(targetDir + "templateResult.xlsx"), true); writer.close(); } @@ -51,7 +50,7 @@ public class TemplateWriterTest { writer.writeRow(createRow(), false); } - writer.flush(FileUtil.file(targetDir + "templateWithFooterResult.xlsx"), true); + //writer.flush(FileUtil.file(targetDir + "templateWithFooterResult.xlsx"), true); writer.close(); } @@ -73,7 +72,7 @@ public class TemplateWriterTest { writer.writeRow(createRow(), false); } - writer.flush(FileUtil.file(targetDir + "templateWithFooterResult.xlsx"), true); + //writer.flush(FileUtil.file(targetDir + "templateWithFooterNoneOneLineResult.xlsx"), true); writer.close(); }