This commit is contained in:
Looly 2022-03-19 11:13:33 +08:00
parent 85d8c3f0ac
commit 15a0662da5
8 changed files with 165 additions and 139 deletions

View File

@ -2,7 +2,7 @@
# 🚀Changelog
-------------------------------------------------------------------------------------------------------------
# 5.7.23 (2022-03-17)
# 5.7.23 (2022-03-19)
### 🐣新特性
* 【http 】 HttpRequest.form采用TableMap方式issue#I4W427@Gitee
@ -20,6 +20,7 @@
* 【core 】 Dict增加setFields方法pr#578@Gitee
* 【db 】 新加db.meta的索引相关接口pr#563@Gitee
* 【db 】 Oracle中Column#typeName后的长度去掉pr#563@Gitee
* 【poi 】 优化ExcelReader采用只读模式pr#2204@Gitee
*
### 🐞Bug修复
* 【core 】 修复ObjectUtil.hasNull传入null返回true的问题pr#555@Gitee

View File

@ -10,7 +10,9 @@ import java.io.File;
import java.io.OutputStream;
/**
* 大数据量Excel写出
* 大数据量Excel写出只支持XLSXExcel07版本<br>
* 通过封装{@link SXSSFWorkbook}限制对滑动窗口中的行的访问来实现其低内存使用<br>
* 注意如果写出数据大于滑动窗口大小就会写出到临时文件此时写出的数据无法访问和编辑
*
* @author looly
* @since 4.1.13
@ -25,6 +27,7 @@ public class BigExcelWriter extends ExcelWriter {
private boolean isFlushed;
// -------------------------------------------------------------------------- Constructor start
/**
* 构造默认生成xlsx格式的Excel文件<br>
* 此构造不传入写出的Excel文件路径只能调用{@link #flush(java.io.OutputStream)}方法写出到流<br>
@ -45,6 +48,21 @@ public class BigExcelWriter extends ExcelWriter {
this(WorkbookUtil.createSXSSFBook(rowAccessWindowSize), null);
}
/**
* 构造<br>
* 此构造不传入写出的Excel文件路径只能调用{@link #flush(java.io.OutputStream)}方法写出到流<br>
* 若写出到文件需要调用{@link #flush(File)} 写出到文件
*
* @param rowAccessWindowSize 在内存中的行数-1表示不限制此时需要手动刷出
* @param compressTmpFiles 是否使用Gzip压缩临时文件
* @param useSharedStringsTable 是否使用共享字符串表一般大量重复字符串时开启可节省内存
* @param sheetName 写出的sheet名称
* @since 5.7.23
*/
public BigExcelWriter(int rowAccessWindowSize, boolean compressTmpFiles, boolean useSharedStringsTable, String sheetName) {
this(WorkbookUtil.createSXSSFBook(rowAccessWindowSize, compressTmpFiles, useSharedStringsTable), sheetName);
}
/**
* 构造默认写出到第一个sheet第一个sheet名为sheet1
*
@ -60,7 +78,7 @@ public class BigExcelWriter extends ExcelWriter {
* 若写出到文件需要调用{@link #flush(File)} 写出到文件
*
* @param rowAccessWindowSize 在内存中的行数
* @param sheetName sheet名第一个sheet名并写出到此sheet例如sheet1
* @param sheetName sheet名第一个sheet名并写出到此sheet例如sheet1
* @since 4.1.8
*/
public BigExcelWriter(int rowAccessWindowSize, String sheetName) {
@ -71,7 +89,7 @@ public class BigExcelWriter extends ExcelWriter {
* 构造
*
* @param destFilePath 目标文件路径可以不存在
* @param sheetName sheet名第一个sheet名并写出到此sheet例如sheet1
* @param sheetName sheet名第一个sheet名并写出到此sheet例如sheet1
*/
public BigExcelWriter(String destFilePath, String sheetName) {
this(FileUtil.file(destFilePath), sheetName);
@ -89,7 +107,7 @@ public class BigExcelWriter extends ExcelWriter {
/**
* 构造
*
* @param destFile 目标文件可以不存在
* @param destFile 目标文件可以不存在
* @param sheetName sheet名做为第一个sheet名并写出到此sheet例如sheet1
*/
public BigExcelWriter(File destFile, String sheetName) {
@ -102,7 +120,7 @@ public class BigExcelWriter extends ExcelWriter {
* 此构造不传入写出的Excel文件路径只能调用{@link #flush(java.io.OutputStream)}方法写出到流<br>
* 若写出到文件还需调用{@link #setDestFile(File)}方法自定义写出的文件然后调用{@link #flush()}方法写出到文件
*
* @param workbook {@link SXSSFWorkbook}
* @param workbook {@link SXSSFWorkbook}
* @param sheetName sheet名做为第一个sheet名并写出到此sheet例如sheet1
*/
public BigExcelWriter(SXSSFWorkbook workbook, String sheetName) {
@ -125,7 +143,7 @@ public class BigExcelWriter extends ExcelWriter {
@Override
public BigExcelWriter autoSizeColumn(int columnIndex) {
final SXSSFSheet sheet = (SXSSFSheet)this.sheet;
final SXSSFSheet sheet = (SXSSFSheet) this.sheet;
sheet.trackColumnForAutoSizing(columnIndex);
super.autoSizeColumn(columnIndex);
sheet.untrackColumnForAutoSizing(columnIndex);
@ -134,7 +152,7 @@ public class BigExcelWriter extends ExcelWriter {
@Override
public BigExcelWriter autoSizeColumnAll() {
final SXSSFSheet sheet = (SXSSFSheet)this.sheet;
final SXSSFSheet sheet = (SXSSFSheet) this.sheet;
sheet.trackAllColumnsForAutoSizing();
super.autoSizeColumnAll();
sheet.untrackAllColumnsForAutoSizing();
@ -143,7 +161,7 @@ public class BigExcelWriter extends ExcelWriter {
@Override
public ExcelWriter flush(OutputStream out, boolean isCloseOut) throws IORuntimeException {
if(false == isFlushed){
if (false == isFlushed) {
isFlushed = true;
return super.flush(out, isCloseOut);
}

View File

@ -17,8 +17,11 @@ import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.Closeable;
import java.io.File;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* Excel基础类用于抽象ExcelWriter和ExcelReader中共用部分的对象和方法
@ -32,6 +35,10 @@ public class ExcelBase<T extends ExcelBase<T>> implements Closeable {
* 是否被关闭
*/
protected boolean isClosed;
/**
* 目标文件如果用户读取为流或自行创建的Workbook或Sheet,此参数为{@code null}
*/
protected File destFile;
/**
* 工作簿
*/
@ -40,6 +47,10 @@ public class ExcelBase<T extends ExcelBase<T>> implements Closeable {
* Excel中对应的Sheet
*/
protected Sheet sheet;
/**
* 标题行别名
*/
protected Map<String, String> headerAlias;
/**
* 构造
@ -171,7 +182,6 @@ public class ExcelBase<T extends ExcelBase<T>> implements Closeable {
* @return this
* @since 5.7.10
*/
@SuppressWarnings("unchecked")
public T cloneSheet(int sheetIndex, String newSheetName, boolean setAsCurrentSheet) {
Sheet sheet;
if (this.workbook instanceof XSSFWorkbook) {
@ -184,6 +194,7 @@ public class ExcelBase<T extends ExcelBase<T>> implements Closeable {
if (setAsCurrentSheet) {
this.sheet = sheet;
}
//noinspection unchecked
return (T) this;
}
@ -499,4 +510,66 @@ public class ExcelBase<T extends ExcelBase<T>> implements Closeable {
this.workbook = null;
this.isClosed = true;
}
/**
* 获得标题行的别名Map
*
* @return 别名Map
*/
public Map<String, String> getHeaderAlias() {
return headerAlias;
}
/**
* 设置标题行的别名Map
*
* @param headerAlias 别名Map
* @return this
*/
public T setHeaderAlias(Map<String, String> headerAlias) {
this.headerAlias = headerAlias;
//noinspection unchecked
return (T) this;
}
/**
* 增加标题别名
*
* @param header 标题
* @param alias 别名
* @return this
*/
public T addHeaderAlias(String header, String alias) {
Map<String, String> headerAlias = this.headerAlias;
if (null == headerAlias) {
headerAlias = new LinkedHashMap<>();
}
this.headerAlias = headerAlias;
this.headerAlias.put(header, alias);
//noinspection unchecked
return (T) this;
}
/**
* 去除标题别名
*
* @param header 标题
* @return this
*/
public T removeHeaderAlias(String header) {
this.headerAlias.remove(header);
//noinspection unchecked
return (T) this;
}
/**
* 清空标题别名key为Map中的keyvalue为别名
*
* @return this
*/
public T clearHeaderAlias() {
this.headerAlias = null;
//noinspection unchecked
return (T) this;
}
}

View File

@ -18,7 +18,6 @@ import org.apache.poi.ss.usermodel.Workbook;
import java.io.File;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -39,11 +38,6 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
* 单元格值处理接口
*/
private CellEditor cellEditor;
/**
* 标题别名
*/
private Map<String, String> headerAlias = new HashMap<>();
// ------------------------------------------------------------------------------------------------------- Constructor start
/**
@ -57,27 +51,29 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
}
/**
* 构造
* 构造读写方式读取
*
* @param bookFile Excel文件
* @param sheetIndex sheet序号0表示第一个sheet
*/
public ExcelReader(File bookFile, int sheetIndex) {
this(WorkbookUtil.createBook(bookFile, true), sheetIndex);
this.destFile = bookFile;
}
/**
* 构造
* 构造读写方式读取
*
* @param bookFile Excel文件
* @param sheetName sheet名第一个默认是sheet1
*/
public ExcelReader(File bookFile, String sheetName) {
this(WorkbookUtil.createBook(bookFile, true), sheetName);
this.destFile = bookFile;
}
/**
* 构造
* 构造只读方式读取
*
* @param bookStream Excel文件的流
* @param sheetIndex sheet序号0表示第一个sheet
@ -87,7 +83,7 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
}
/**
* 构造
* 构造只读方式读取
*
* @param bookStream Excel文件的流
* @param sheetName sheet名第一个默认是sheet1
@ -159,49 +155,6 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
this.cellEditor = cellEditor;
return this;
}
/**
* 获得标题行的别名Map
*
* @return 别名Map
*/
public Map<String, String> getHeaderAlias() {
return headerAlias;
}
/**
* 设置标题行的别名Map
*
* @param headerAlias 别名Map
* @return this
*/
public ExcelReader setHeaderAlias(Map<String, String> headerAlias) {
this.headerAlias = headerAlias;
return this;
}
/**
* 增加标题别名
*
* @param header 标题
* @param alias 别名
* @return this
*/
public ExcelReader addHeaderAlias(String header, String alias) {
this.headerAlias.put(header, alias);
return this;
}
/**
* 去除标题别名
*
* @param header 标题
* @return this
*/
public ExcelReader removeHeaderAlias(String header) {
this.headerAlias.remove(header);
return this;
}
// ------------------------------------------------------------------------------------------------------- Getters and Setters end
/**
@ -452,13 +405,14 @@ public class ExcelReader extends ExcelBase<ExcelReader> {
/**
* 获取Excel写出器<br>
* 在读取Excel并做一定编辑后获取写出器写出
* 在读取Excel并做一定编辑后获取写出器写出<br>
* 注意只读方式下此方法无效
*
* @return {@link ExcelWriter}
* @since 4.0.6
*/
public ExcelWriter getWriter() {
return new ExcelWriter(this.sheet);
return ExcelUtil.getWriter(this.destFile, this.sheet.getSheetName());
}
// ------------------------------------------------------------------------------------------------------- Private methods start

View File

@ -59,18 +59,10 @@ import java.util.concurrent.atomic.AtomicInteger;
*/
public class ExcelWriter extends ExcelBase<ExcelWriter> {
/**
* 目标文件
*/
protected File destFile;
/**
* 当前行
*/
private AtomicInteger currentRow = new AtomicInteger(0);
/**
* 标题行别名
*/
private Map<String, String> headerAlias;
/**
* 是否只保留别名对应的字段
*/
@ -462,31 +454,26 @@ public class ExcelWriter extends ExcelBase<ExcelWriter> {
return this;
}
/**
* 设置标题别名key为Map中的keyvalue为别名
*
* @param headerAlias 标题别名
* @return this
* @since 3.2.1
*/
//region header alias
@Override
public ExcelWriter setHeaderAlias(Map<String, String> headerAlias) {
this.headerAlias = headerAlias;
// 新增别名时清除比较器缓存
this.aliasComparator = null;
return this;
return super.setHeaderAlias(headerAlias);
}
/**
* 清空标题别名key为Map中的keyvalue为别名
*
* @return this
* @since 4.5.4
*/
@Override
public ExcelWriter clearHeaderAlias() {
this.headerAlias = null;
// 清空别名时清除比较器缓存
this.aliasComparator = null;
return this;
return super.clearHeaderAlias();
}
@Override
public ExcelWriter addHeaderAlias(String name, String alias) {
// 新增别名时清除比较器缓存
this.aliasComparator = null;
return super.addHeaderAlias(name, alias);
}
/**
@ -500,26 +487,7 @@ public class ExcelWriter extends ExcelBase<ExcelWriter> {
this.onlyAlias = isOnlyAlias;
return this;
}
/**
* 增加标题别名
*
* @param name 原标题
* @param alias 别名
* @return this
* @since 4.1.5
*/
public ExcelWriter addHeaderAlias(String name, String alias) {
Map<String, String> headerAlias = this.headerAlias;
if (null == headerAlias) {
headerAlias = new LinkedHashMap<>();
}
this.headerAlias = headerAlias;
headerAlias.put(name, alias);
// 新增别名时清除比较器缓存
this.aliasComparator = null;
return this;
}
//endregion
/**
* 设置窗口冻结之前冻结的窗口会被覆盖如果rowSplit为0表示取消冻结

View File

@ -71,7 +71,7 @@ public class WorkbookUtil {
}
/**
* 创建工作簿用于Excel写出
* 创建工作簿用于Excel写出读写模式
*
* <pre>
* 1. excelFile为null时直接返回一个空的工作簿默认xlsx格式
@ -113,6 +113,7 @@ public class WorkbookUtil {
* @param password Excel工作簿密码如果无密码传{@code null}
* @param readOnly 是否只读模式打开true:不可编辑false:可编辑
* @return {@link Workbook}
* @since 5.7.23
*/
public static Workbook createBook(File excelFile, String password, boolean readOnly) {
try {
@ -123,7 +124,7 @@ public class WorkbookUtil {
}
/**
* 创建或加载工作簿
* 创建或加载工作簿只读模式
*
* @param in Excel输入流
* @return {@link Workbook}
@ -133,7 +134,7 @@ public class WorkbookUtil {
}
/**
* 创建或加载工作簿
* 创建或加载工作簿只读模式
*
* @param in Excel输入流使用完毕自动关闭流
* @param password 密码
@ -151,7 +152,7 @@ public class WorkbookUtil {
}
/**
* 根据文件类型创建新的工作簿文件路径
* 创建新的空白Excel工作簿
*
* @param isXlsx 是否为xlsx格式的Excel
* @return {@link Workbook}
@ -184,7 +185,7 @@ public class WorkbookUtil {
* @param excelFilePath Excel文件路径绝对路径或相对于ClassPath路径
* @param readOnly 是否只读模式打开true:不可编辑false:可编辑
* @return {@link SXSSFWorkbook}
* @since 4.1.13
* @since 5.7.23
*/
public static SXSSFWorkbook createSXSSFBook(String excelFilePath, boolean readOnly) {
return createSXSSFBook(FileUtil.file(excelFilePath), null, readOnly);
@ -207,7 +208,7 @@ public class WorkbookUtil {
* @param excelFile Excel文件
* @param readOnly 是否只读模式打开true:不可编辑false:可编辑
* @return {@link SXSSFWorkbook}
* @since 4.1.13
* @since 5.7.23
*/
public static SXSSFWorkbook createSXSSFBook(File excelFile, boolean readOnly) {
return createSXSSFBook(excelFile, null, readOnly);
@ -228,20 +229,20 @@ public class WorkbookUtil {
/**
* 创建或加载SXSSFWorkbook工作簿
* 创建或加载{@link SXSSFWorkbook}工作簿
*
* @param excelFile Excel文件
* @param password Excel工作簿密码如果无密码传{@code null}
* @param readOnly 是否只读模式打开true:不可编辑false:可编辑
* @return {@link SXSSFWorkbook}
* @since 4.1.13
* @since 5.7.23
*/
public static SXSSFWorkbook createSXSSFBook(File excelFile, String password, boolean readOnly) {
return toSXSSFBook(createBook(excelFile, password, readOnly));
}
/**
* 创建或加载SXSSFWorkbook工作簿
* 创建或加载{@link SXSSFWorkbook}工作簿只读模式
*
* @param in Excel输入流
* @return {@link SXSSFWorkbook}
@ -252,7 +253,7 @@ public class WorkbookUtil {
}
/**
* 创建或加载SXSSFWorkbook工作簿
* 创建或加载{@link SXSSFWorkbook}工作簿只读模式
*
* @param in Excel输入流
* @param password 密码
@ -264,7 +265,7 @@ public class WorkbookUtil {
}
/**
* 创建SXSSFWorkbook用于大批量数据写出
* 创建空的{@link SXSSFWorkbook}用于大批量数据写出
*
* @return {@link SXSSFWorkbook}
* @since 4.1.13
@ -274,16 +275,29 @@ public class WorkbookUtil {
}
/**
* 创建SXSSFWorkbook用于大批量数据写出
* 创建空的{@link SXSSFWorkbook}用于大批量数据写出
*
* @param rowAccessWindowSize 在内存中的行数
* @return {@link Workbook}
* @param rowAccessWindowSize 在内存中的行数-1表示不限制此时需要手动刷出
* @return {@link SXSSFWorkbook}
* @since 4.1.13
*/
public static SXSSFWorkbook createSXSSFBook(int rowAccessWindowSize) {
return new SXSSFWorkbook(rowAccessWindowSize);
}
/**
* 创建空的{@link SXSSFWorkbook}用于大批量数据写出
*
* @param rowAccessWindowSize 在内存中的行数-1表示不限制此时需要手动刷出
* @param compressTmpFiles 是否使用Gzip压缩临时文件
* @param useSharedStringsTable 是否使用共享字符串表一般大量重复字符串时开启可节省内存
* @return {@link SXSSFWorkbook}
* @since 5.7.23
*/
public static SXSSFWorkbook createSXSSFBook(int rowAccessWindowSize, boolean compressTmpFiles, boolean useSharedStringsTable) {
return new SXSSFWorkbook(null, rowAccessWindowSize, compressTmpFiles, useSharedStringsTable);
}
/**
* 将Excel Workbook刷出到输出流不关闭流
*

View File

@ -8,7 +8,7 @@ import cn.hutool.poi.excel.cell.CellEditor;
import org.apache.poi.ss.usermodel.Sheet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@ -40,7 +40,7 @@ public abstract class AbstractSheetReader<T> implements SheetReader<T> {
/**
* 标题别名
*/
private Map<String, String> headerAlias = new HashMap<>();
private Map<String, String> headerAlias;
/**
* 构造
@ -88,6 +88,11 @@ public abstract class AbstractSheetReader<T> implements SheetReader<T> {
* @param alias 别名
*/
public void addHeaderAlias(String header, String alias) {
Map<String, String> headerAlias = this.headerAlias;
if (null == headerAlias) {
headerAlias = new LinkedHashMap<>();
}
this.headerAlias = headerAlias;
this.headerAlias.put(header, alias);
}
@ -124,7 +129,10 @@ public abstract class AbstractSheetReader<T> implements SheetReader<T> {
}
final String header = headerObj.toString();
return ObjectUtil.defaultIfNull(this.headerAlias.get(header), header);
if(null != this.headerAlias){
return ObjectUtil.defaultIfNull(this.headerAlias.get(header), header);
}
return header;
}
/**

View File

@ -3,10 +3,6 @@ package cn.hutool.poi.excel;
import cn.hutool.poi.excel.cell.CellLocation;
import org.junit.Assert;
import org.junit.Test;
import org.junit.Ignore;
import java.util.List;
import java.util.Map;
public class ExcelUtilTest {
@ -48,16 +44,10 @@ public class ExcelUtilTest {
}
@Test
@Ignore
public void readAndWriteTest() {
String filepath = "d:\\test/select.xls";
ExcelWriter writer = ExcelUtil.getWriter(filepath);
ExcelReader reader = ExcelUtil.getReader("aaa.xlsx");
ExcelWriter writer = reader.getWriter();
writer.writeCellValue(1, 2, "设置值");
writer.close();
ExcelReader reader = ExcelUtil.getReader(filepath);
List<Map<String, Object>> map = reader.readAll();
reader.close();
Assert.assertNotNull(map);
Assert.assertFalse(map.isEmpty());
}
}