diff --git a/CHANGELOG.md b/CHANGELOG.md index cc96caed4..52ea7394a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,10 +11,13 @@ * 【core】 MapUtil、CollUtil增加emptyIfNull(issue#502@Github) * 【core】 增加emptyIfNull等(issue#503@Github) * 【setting】 Props增加toBean方法(issue#499@Github) +* 【poi】 CellUtil增加getMergedRegionValue方法,ExcelWriter增加getDisposition方法 +* 【http】 HttpBase增加headerMap方法 ### Bug修复 * 【http】 修复HttpRquest中body方法长度计算问题(issue#I10UPG@Gitee) * 【system】 修复获取本地IP问题(pr#65@Gitee) +* 【poi】 修复设置单元格样式无效问题 ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-http/src/main/java/cn/hutool/http/HttpBase.java b/hutool-http/src/main/java/cn/hutool/http/HttpBase.java index 3893fbd36..33ac073ae 100644 --- a/hutool-http/src/main/java/cn/hutool/http/HttpBase.java +++ b/hutool-http/src/main/java/cn/hutool/http/HttpBase.java @@ -134,6 +134,25 @@ public abstract class HttpBase { return header(name, value, true); } + /** + * 设置请求头 + * + * @param headers 请求头 + * @param isOverride 是否覆盖已有头信息 + * @return this + * @since 4.6.3 + */ + public T headerMap(Map headers, boolean isOverride) { + if(CollectionUtil.isEmpty(headers)) { + return (T)this; + } + + for (Entry entry : headers.entrySet()) { + this.header(entry.getKey(), StrUtil.nullToEmpty(entry.getValue()), isOverride); + } + return (T)this; + } + /** * 设置请求头
* 不覆盖原有请求头 @@ -146,8 +165,7 @@ public abstract class HttpBase { } /** - * 设置请求头
- * 不覆盖原有请求头 + * 设置请求头 * * @param headers 请求头 * @param isOverride 是否覆盖已有头信息 diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelBase.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelBase.java index 28ff2079a..2d6c736ee 100644 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelBase.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelBase.java @@ -14,6 +14,7 @@ import org.apache.poi.xssf.usermodel.XSSFSheet; import cn.hutool.core.io.IoUtil; import cn.hutool.core.lang.Assert; import cn.hutool.poi.excel.cell.CellUtil; +import cn.hutool.poi.excel.style.StyleUtil; /** * Excel基础类,用于抽象ExcelWriter和ExcelReader中共用部分的对象和方法 @@ -198,45 +199,74 @@ public class ExcelBase> implements Closeable { * @since 4.1.4 */ public CellStyle getOrCreateCellStyle(int x, int y) { + final CellStyle cellStyle = getOrCreateCell(x, y).getCellStyle(); + return StyleUtil.isNullOrDefaultStyle(this.workbook, cellStyle) ? createCellStyle(x, y) : cellStyle; + } + + /** + * 为指定单元格创建样式,返回样式后可以设置样式内容 + * + * @param x X坐标,从0计数,既列号 + * @param y Y坐标,从0计数,既行号 + * @return {@link CellStyle} + * @since 4.6.3 + */ + public CellStyle createCellStyle(int x, int y) { final Cell cell = getOrCreateCell(x, y); - CellStyle cellStyle = cell.getCellStyle(); - if (null == cellStyle) { - cellStyle = this.workbook.createCellStyle(); - cell.setCellStyle(cellStyle); - } + final CellStyle cellStyle = this.workbook.createCellStyle(); + cell.setCellStyle(cellStyle); return cellStyle; } /** - * 获取或创建某一行的样式,返回样式后可以设置样式内容 + * 获取或创建某一行的样式,返回样式后可以设置样式内容
+ * 需要注意,此方法返回行样式,设置背景色在单元格设置值后会被覆盖,需要单独设置其单元格的样式。 * * @param y Y坐标,从0计数,既行号 * @return {@link CellStyle} * @since 4.1.4 */ public CellStyle getOrCreateRowStyle(int y) { - final Row row = getOrCreateRow(y); - CellStyle rowStyle = row.getRowStyle(); - if (null == rowStyle) { - rowStyle = this.workbook.createCellStyle(); - row.setRowStyle(rowStyle); - } + CellStyle rowStyle = getOrCreateRow(y).getRowStyle(); + return StyleUtil.isNullOrDefaultStyle(this.workbook, rowStyle) ? createRowStyle(y) : rowStyle; + } + + /** + * 创建某一行的样式,返回样式后可以设置样式内容 + * + * @param y Y坐标,从0计数,既行号 + * @return {@link CellStyle} + * @since 4.6.3 + */ + public CellStyle createRowStyle(int y) { + final CellStyle rowStyle = this.workbook.createCellStyle(); + getOrCreateRow(y).setRowStyle(rowStyle); return rowStyle; } /** - * 获取或创建某一行的样式,返回样式后可以设置样式内容 + * 获取或创建某一行的样式,返回样式后可以设置样式内容
+ * 需要注意,此方法返回行样式,设置背景色在单元格设置值后会被覆盖,需要单独设置其单元格的样式。 * * @param x X坐标,从0计数,既列号 * @return {@link CellStyle} * @since 4.1.4 */ public CellStyle getOrCreateColumnStyle(int x) { - CellStyle columnStyle = this.sheet.getColumnStyle(x); - if (null == columnStyle) { - columnStyle = this.workbook.createCellStyle(); - this.sheet.setDefaultColumnStyle(x, columnStyle); - } + final CellStyle columnStyle = this.sheet.getColumnStyle(x); + return StyleUtil.isNullOrDefaultStyle(this.workbook, columnStyle) ? createColumnStyle(x) : columnStyle; + } + + /** + * 创建某一行的样式,返回样式后可以设置样式内容 + * + * @param x X坐标,从0计数,既列号 + * @return {@link CellStyle} + * @since 4.6.3 + */ + public CellStyle createColumnStyle(int x) { + final CellStyle columnStyle = this.workbook.createCellStyle(); + this.sheet.setDefaultColumnStyle(x, columnStyle); return columnStyle; } diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelWriter.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelWriter.java index 7c7308467..e20edbfdf 100644 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelWriter.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelWriter.java @@ -3,6 +3,7 @@ package cn.hutool.poi.excel; import java.io.File; import java.io.IOException; import java.io.OutputStream; +import java.nio.charset.Charset; import java.util.Comparator; import java.util.LinkedHashMap; import java.util.Map; @@ -34,6 +35,10 @@ import cn.hutool.core.io.IORuntimeException; import cn.hutool.core.io.IoUtil; import cn.hutool.core.lang.Assert; import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.CharsetUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.URLUtil; import cn.hutool.poi.excel.cell.CellUtil; import cn.hutool.poi.excel.style.Align; @@ -263,6 +268,17 @@ public class ExcelWriter extends ExcelBase { this.sheet.autoSizeColumn(columnIndex, useMergedCells); return this; } + + /** + * 禁用默认样式 + * + * @return this + * @see #setStyleSet(StyleSet) + * @since 4.6.3 + */ + public ExcelWriter disableDefaultStyle() { + return setStyleSet(null); + } /** * 设置样式集,如果不使用样式,传入{@code null} @@ -319,6 +335,31 @@ public class ExcelWriter extends ExcelBase { public int getCurrentRow() { return this.currentRow.get(); } + + /** + * 获取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.CHARSET_UTF_8; + } + + if(StrUtil.isBlank(fileName)) { + // 未提供文件名使用随机UUID作为文件名 + fileName = IdUtil.fastSimpleUUID(); + } + + fileName = StrUtil.addSuffixIfNot(URLUtil.encodeAll(fileName, charset), isXlsx() ? ".xlsx" : ".xls"); + return StrUtil.format("attachment; filename=\"{}\"; filename*={}''{}", fileName, charset.name(), fileName); + } /** * 设置当前所在行 @@ -824,7 +865,7 @@ public class ExcelWriter extends ExcelBase { * @param y Y坐标,从0计数,既行号 * @return {@link CellStyle} * @since 4.0.9 - * @deprecated 请使用{@link #getOrCreateCellStyle(int, int)} + * @deprecated 请使用{@link #createCellStyle(int, int)} */ @Deprecated public CellStyle createStyleForCell(int x, int y) { @@ -833,6 +874,25 @@ public class ExcelWriter extends ExcelBase { cell.setCellStyle(cellStyle); return cellStyle; } + + /** + * 设置某个单元格的样式
+ * 此方法用于多个单元格共享样式的情况
+ * 可以调用{@link #getOrCreateCellStyle(int, int)} 方法创建或取得一个样式对象。 + * + *

+ * 需要注意的是,共享样式会共享同一个{@link CellStyle},一个单元格样式改变,全部改变。 + * + * @param x X坐标,从0计数,既列号 + * @param y Y坐标,从0计数,既行号 + * @return this + * @since 4.6.3 + */ + public ExcelWriter setStyle(CellStyle style, int x, int y) { + final Cell cell = getOrCreateCell(x, y); + cell.setCellStyle(style); + return this; + } /** * 创建字体 diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellUtil.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellUtil.java index d8adc5542..14b867fe9 100644 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellUtil.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/cell/CellUtil.java @@ -3,6 +3,7 @@ package cn.hutool.poi.excel.cell; import java.math.BigDecimal; import java.util.Calendar; import java.util.Date; +import java.util.List; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; @@ -13,6 +14,7 @@ import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.util.CellRangeAddress; import org.apache.poi.ss.util.RegionUtil; +import org.apache.poi.ss.util.SheetUtil; import cn.hutool.core.date.DateUtil; import cn.hutool.core.util.StrUtil; @@ -26,7 +28,18 @@ import cn.hutool.poi.excel.editors.TrimEditor; * @since 4.0.7 */ public class CellUtil { - + + /** + * 获取单元格值 + * + * @param cell {@link Cell}单元格 + * @return 值,类型可能为:Date、Double、Boolean、String + * @since 4.6.3 + */ + public static Object getCellValue(Cell cell) { + return getCellValue(cell, false); + } + /** * 获取单元格值 * @@ -121,7 +134,11 @@ public class CellUtil { * @param isHeader 是否为标题单元格 */ public static void setCellValue(Cell cell, Object value, StyleSet styleSet, boolean isHeader) { - if(null != styleSet) { + if(null == cell) { + return; + } + + if (null != styleSet) { final CellStyle headCellStyle = styleSet.getHeadCellStyle(); final CellStyle cellStyle = styleSet.getCellStyle(); if (isHeader && null != headCellStyle) { @@ -148,7 +165,7 @@ public class CellUtil { } else if (value instanceof RichTextString) { cell.setCellValue((RichTextString) value); } else if (value instanceof Number) { - if ((value instanceof Double || value instanceof Float || value instanceof BigDecimal) && null != styleSet && null != styleSet.getCellStyleForNumber()) { + if ((value instanceof Double || value instanceof Float || value instanceof BigDecimal) && null != styleSet && null != styleSet.getCellStyleForNumber()) { cell.setCellStyle(styleSet.getCellStyleForNumber()); } cell.setCellValue(((Number) value).doubleValue()); @@ -221,6 +238,40 @@ public class CellUtil { return sheet.addMergedRegion(cellRangeAddress); } + /** + * 获取合并单元格的值
+ * 传入的x,y坐标(列行数)可以是合并单元格范围内的任意一个单元格 + * + * + * @param sheet {@link Sheet} + * @param y 行号,从0开始,可以是合并单元格范围中的任意一行 + * @param x 列号,从0开始,可以是合并单元格范围中的任意一列 + * @return 合并单元格的值 + * @since 4.6.3 + */ + public static Object getMergedRegionValue(Sheet sheet, int x, int y) { + final List addrs = sheet.getMergedRegions(); + + int firstColumn; + int lastColumn; + int firstRow; + int lastRow; + for (CellRangeAddress ca : addrs) { + firstColumn = ca.getFirstColumn(); + lastColumn = ca.getLastColumn(); + firstRow = ca.getFirstRow(); + lastRow = ca.getLastRow(); + + if (y >= firstRow && y <= lastRow) { + if (x >= firstColumn && x <= lastColumn) { + return getCellValue(SheetUtil.getCell(sheet, firstRow, firstColumn)); + } + } + } + + return null; + } + // -------------------------------------------------------------------------------------------------------------- Private method start /** * 获取数字类型的单元格值 diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/style/StyleUtil.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/style/StyleUtil.java index 450875259..8160d3556 100644 --- a/hutool-poi/src/main/java/cn/hutool/poi/excel/style/StyleUtil.java +++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/style/StyleUtil.java @@ -175,4 +175,18 @@ public class StyleUtil { setColor(cellStyle, IndexedColors.GREY_25_PERCENT, FillPatternType.SOLID_FOREGROUND); return cellStyle; } + + /** + * 给定样式是否为null(无样式)或默认样式,默认样式为workbook.getCellStyleAt(0) + * @param workbook 工作簿 + * @param style 被检查的样式 + * @return 是否为null(无样式)或默认样式 + * @since 4.6.3 + */ + public static boolean isNullOrDefaultStyle(Workbook workbook, CellStyle style) { + if(null == style || style.equals(workbook.getCellStyleAt(0))) { + return true; + } + return false; + } } diff --git a/hutool-poi/src/test/java/cn/hutool/poi/excel/test/ExcelWriteTest.java b/hutool-poi/src/test/java/cn/hutool/poi/excel/test/ExcelWriteTest.java index 11eeb5ea5..92d8ead79 100644 --- a/hutool-poi/src/test/java/cn/hutool/poi/excel/test/ExcelWriteTest.java +++ b/hutool-poi/src/test/java/cn/hutool/poi/excel/test/ExcelWriteTest.java @@ -208,6 +208,33 @@ public class ExcelWriteTest { // 关闭writer,释放内存 writer.close(); } + + @Test + @Ignore + public void writeMapWithStyleTest() { + Map row1 = MapUtil.newHashMap(true); + row1.put("姓名", "张三"); + row1.put("年龄", 23); + row1.put("成绩", 88.32); + row1.put("是否合格", true); + row1.put("考试日期", DateUtil.date()); + + // 通过工具类创建writer + String path = "f:/test/writeMapWithStyleTest.xlsx"; + FileUtil.del(path); + ExcelWriter writer = ExcelUtil.getWriter(path); + writer.setStyleSet(null); + + // 一次性写出内容,使用默认样式 + writer.writeRow(row1, true); + + // 设置某个单元格样式 + CellStyle orCreateRowStyle = writer.getOrCreateCellStyle(0, 1); + StyleUtil.setColor(orCreateRowStyle,IndexedColors.RED.getIndex(),FillPatternType.SOLID_FOREGROUND ); + + // 关闭writer,释放内存 + writer.close(); + } @Test @Ignore @@ -278,7 +305,7 @@ public class ExcelWriteTest { } @Test -// @Ignore + @Ignore public void writeMapOnlyAliasTest2() { Map row1 = new LinkedHashMap<>(); row1.put("name", "张三");