serv-excel工具
This commit is contained in:
@@ -18,4 +18,19 @@
|
|||||||
<maven.compiler.target>21</maven.compiler.target>
|
<maven.compiler.target>21</maven.compiler.target>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<!-- Apache POI for Excel -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.poi</groupId>
|
||||||
|
<artifactId>poi</artifactId>
|
||||||
|
<version>${poi.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.poi</groupId>
|
||||||
|
<artifactId>poi-ooxml</artifactId>
|
||||||
|
<version>${poi.version}</version>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
@@ -0,0 +1,208 @@
|
|||||||
|
package org.xyzh.common.utils.excel;
|
||||||
|
|
||||||
|
import org.xyzh.common.utils.validation.ValidationParam;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Excel列映射配置
|
||||||
|
* @filename ExcelColumnMapping.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-10-16
|
||||||
|
*/
|
||||||
|
public class ExcelColumnMapping {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Excel列名(表头名称)
|
||||||
|
*/
|
||||||
|
private String columnName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Excel列索引(从0开始,优先级高于列名)
|
||||||
|
*/
|
||||||
|
private Integer columnIndex;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 对象字段名
|
||||||
|
*/
|
||||||
|
private String fieldName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 字段类型
|
||||||
|
*/
|
||||||
|
private Class<?> fieldType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 是否必填
|
||||||
|
*/
|
||||||
|
private boolean required = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 默认值
|
||||||
|
*/
|
||||||
|
private String defaultValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 日期格式(当字段类型为Date时使用)
|
||||||
|
*/
|
||||||
|
private String dateFormat = "yyyy-MM-dd";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 校验参数列表
|
||||||
|
*/
|
||||||
|
private List<ValidationParam> validationParams;
|
||||||
|
|
||||||
|
private ExcelColumnMapping() {
|
||||||
|
this.validationParams = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getColumnName() {
|
||||||
|
return columnName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Integer getColumnIndex() {
|
||||||
|
return columnIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getFieldName() {
|
||||||
|
return fieldName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Class<?> getFieldType() {
|
||||||
|
return fieldType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isRequired() {
|
||||||
|
return required;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDefaultValue() {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDateFormat() {
|
||||||
|
return dateFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<ValidationParam> getValidationParams() {
|
||||||
|
return validationParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Builder类
|
||||||
|
*/
|
||||||
|
public static class Builder {
|
||||||
|
private ExcelColumnMapping mapping = new ExcelColumnMapping();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置Excel列名
|
||||||
|
*/
|
||||||
|
public Builder columnName(String columnName) {
|
||||||
|
mapping.columnName = columnName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置Excel列索引(从0开始)
|
||||||
|
*/
|
||||||
|
public Builder columnIndex(int columnIndex) {
|
||||||
|
mapping.columnIndex = columnIndex;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置对象字段名
|
||||||
|
*/
|
||||||
|
public Builder fieldName(String fieldName) {
|
||||||
|
mapping.fieldName = fieldName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置字段类型
|
||||||
|
*/
|
||||||
|
public Builder fieldType(Class<?> fieldType) {
|
||||||
|
mapping.fieldType = fieldType;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置是否必填
|
||||||
|
*/
|
||||||
|
public Builder required(boolean required) {
|
||||||
|
mapping.required = required;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置为必填
|
||||||
|
*/
|
||||||
|
public Builder required() {
|
||||||
|
mapping.required = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置默认值
|
||||||
|
*/
|
||||||
|
public Builder defaultValue(String defaultValue) {
|
||||||
|
mapping.defaultValue = defaultValue;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置日期格式
|
||||||
|
*/
|
||||||
|
public Builder dateFormat(String dateFormat) {
|
||||||
|
mapping.dateFormat = dateFormat;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加校验参数
|
||||||
|
*/
|
||||||
|
public Builder addValidation(ValidationParam param) {
|
||||||
|
mapping.validationParams.add(param);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置校验参数列表
|
||||||
|
*/
|
||||||
|
public Builder validations(List<ValidationParam> params) {
|
||||||
|
mapping.validationParams = params;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ExcelColumnMapping build() {
|
||||||
|
if (mapping.fieldName == null || mapping.fieldName.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("字段名不能为空");
|
||||||
|
}
|
||||||
|
if (mapping.columnName == null && mapping.columnIndex == null) {
|
||||||
|
throw new IllegalArgumentException("必须指定列名或列索引");
|
||||||
|
}
|
||||||
|
if (mapping.fieldType == null) {
|
||||||
|
mapping.fieldType = String.class;
|
||||||
|
}
|
||||||
|
return mapping;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Builder builder() {
|
||||||
|
return new Builder();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ExcelColumnMapping{" +
|
||||||
|
"columnName='" + columnName + '\'' +
|
||||||
|
", columnIndex=" + columnIndex +
|
||||||
|
", fieldName='" + fieldName + '\'' +
|
||||||
|
", fieldType=" + (fieldType != null ? fieldType.getSimpleName() : "null") +
|
||||||
|
", required=" + required +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
package org.xyzh.common.utils.excel;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Excel读取结果
|
||||||
|
* @filename ExcelReadResult.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-10-16
|
||||||
|
*/
|
||||||
|
public class ExcelReadResult<T> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 是否成功
|
||||||
|
*/
|
||||||
|
private boolean success;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 成功读取的数据列表
|
||||||
|
*/
|
||||||
|
private List<T> dataList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 失败的行数据(行号 -> 错误信息)
|
||||||
|
*/
|
||||||
|
private Map<Integer, String> errorRowsMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 总行数(不包括表头)
|
||||||
|
*/
|
||||||
|
private int totalRows;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 成功行数
|
||||||
|
*/
|
||||||
|
private int successRows;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 失败行数
|
||||||
|
*/
|
||||||
|
private int errorRowsCount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 错误信息
|
||||||
|
*/
|
||||||
|
private String errorMessage;
|
||||||
|
|
||||||
|
public ExcelReadResult() {
|
||||||
|
this.success = true;
|
||||||
|
this.dataList = new ArrayList<>();
|
||||||
|
this.errorRowsMap = new HashMap<>();
|
||||||
|
this.totalRows = 0;
|
||||||
|
this.successRows = 0;
|
||||||
|
this.errorRowsCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSuccess() {
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSuccess(boolean success) {
|
||||||
|
this.success = success;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<T> getDataList() {
|
||||||
|
return dataList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDataList(List<T> dataList) {
|
||||||
|
this.dataList = dataList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Integer, String> getErrorRows() {
|
||||||
|
return errorRowsMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrorRows(Map<Integer, String> errorRowsMap) {
|
||||||
|
this.errorRowsMap = errorRowsMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getTotalRows() {
|
||||||
|
return totalRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTotalRows(int totalRows) {
|
||||||
|
this.totalRows = totalRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getSuccessRows() {
|
||||||
|
return successRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSuccessRows(int successRows) {
|
||||||
|
this.successRows = successRows;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getErrorRowsCount() {
|
||||||
|
return errorRowsCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrorRowsCount(int errorRowsCount) {
|
||||||
|
this.errorRowsCount = errorRowsCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getErrorMessage() {
|
||||||
|
return errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setErrorMessage(String errorMessage) {
|
||||||
|
this.errorMessage = errorMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addData(T data) {
|
||||||
|
this.dataList.add(data);
|
||||||
|
this.successRows++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addError(int rowNum, String error) {
|
||||||
|
this.errorRowsMap.put(rowNum, error);
|
||||||
|
this.errorRowsCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasErrors() {
|
||||||
|
return this.errorRowsCount > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ExcelReadResult{" +
|
||||||
|
"success=" + success +
|
||||||
|
", totalRows=" + totalRows +
|
||||||
|
", successRows=" + successRows +
|
||||||
|
", errorRows=" + errorRowsCount +
|
||||||
|
", errorMessage='" + errorMessage + '\'' +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,426 @@
|
|||||||
|
package org.xyzh.common.utils.excel;
|
||||||
|
|
||||||
|
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||||
|
import org.apache.poi.ss.usermodel.*;
|
||||||
|
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||||
|
import org.xyzh.common.utils.validation.ValidationParam;
|
||||||
|
import org.xyzh.common.utils.validation.ValidationResult;
|
||||||
|
import org.xyzh.common.utils.validation.ValidationUtils;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Excel读取工具类(非泛型版本)
|
||||||
|
* @filename ExcelReaderUtils.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-10-16
|
||||||
|
*/
|
||||||
|
public class ExcelReaderUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 从文件读取Excel
|
||||||
|
* @param file Excel文件
|
||||||
|
* @param targetClass 目标对象Class
|
||||||
|
* @param columnMappings Excel列映射配置列表
|
||||||
|
* @return ExcelReadResult
|
||||||
|
*/
|
||||||
|
public static ExcelReadResult<Object> readExcel(File file, Class<?> targetClass, List<ExcelColumnMapping> columnMappings) {
|
||||||
|
try (FileInputStream fis = new FileInputStream(file)) {
|
||||||
|
return readExcel(fis, file.getName(), targetClass, columnMappings, new HashMap<>());
|
||||||
|
} catch (Exception e) {
|
||||||
|
ExcelReadResult<Object> result = new ExcelReadResult<>();
|
||||||
|
result.setSuccess(false);
|
||||||
|
result.setErrorMessage("读取Excel文件失败: " + e.getMessage());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 从文件读取Excel(带配置)
|
||||||
|
* @param file Excel文件
|
||||||
|
* @param targetClass 目标对象Class
|
||||||
|
* @param columnMappings Excel列映射配置列表
|
||||||
|
* @param options 配置选项
|
||||||
|
* @return ExcelReadResult
|
||||||
|
*/
|
||||||
|
public static ExcelReadResult<Object> readExcel(File file, Class<?> targetClass,
|
||||||
|
List<ExcelColumnMapping> columnMappings,
|
||||||
|
Map<String, Object> options) {
|
||||||
|
try (FileInputStream fis = new FileInputStream(file)) {
|
||||||
|
return readExcel(fis, file.getName(), targetClass, columnMappings, options);
|
||||||
|
} catch (Exception e) {
|
||||||
|
ExcelReadResult<Object> result = new ExcelReadResult<>();
|
||||||
|
result.setSuccess(false);
|
||||||
|
result.setErrorMessage("读取Excel文件失败: " + e.getMessage());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 从输入流读取Excel
|
||||||
|
* @param inputStream 输入流
|
||||||
|
* @param fileName 文件名
|
||||||
|
* @param targetClass 目标对象Class
|
||||||
|
* @param columnMappings Excel列映射配置列表
|
||||||
|
* @return ExcelReadResult
|
||||||
|
*/
|
||||||
|
public static ExcelReadResult<Object> readExcel(InputStream inputStream, String fileName,
|
||||||
|
Class<?> targetClass,
|
||||||
|
List<ExcelColumnMapping> columnMappings) {
|
||||||
|
return readExcel(inputStream, fileName, targetClass, columnMappings, new HashMap<>());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 从输入流读取Excel(带配置)
|
||||||
|
* @param inputStream 输入流
|
||||||
|
* @param fileName 文件名
|
||||||
|
* @param targetClass 目标对象Class
|
||||||
|
* @param columnMappings Excel列映射配置列表
|
||||||
|
* @param options 配置选项(headerRowIndex, dataStartRowIndex, sheetIndex, sheetName, skipEmptyRow, maxRows, continueOnError)
|
||||||
|
* @return ExcelReadResult
|
||||||
|
*/
|
||||||
|
public static ExcelReadResult<Object> readExcel(InputStream inputStream, String fileName,
|
||||||
|
Class<?> targetClass,
|
||||||
|
List<ExcelColumnMapping> columnMappings,
|
||||||
|
Map<String, Object> options) {
|
||||||
|
ExcelReadResult<Object> result = new ExcelReadResult<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 获取配置
|
||||||
|
int headerRowIndex = (int) options.getOrDefault("headerRowIndex", 0);
|
||||||
|
int dataStartRowIndex = (int) options.getOrDefault("dataStartRowIndex", 1);
|
||||||
|
int sheetIndex = (int) options.getOrDefault("sheetIndex", 0);
|
||||||
|
String sheetName = (String) options.get("sheetName");
|
||||||
|
boolean skipEmptyRow = (boolean) options.getOrDefault("skipEmptyRow", true);
|
||||||
|
int maxRows = (int) options.getOrDefault("maxRows", 0);
|
||||||
|
boolean continueOnError = (boolean) options.getOrDefault("continueOnError", true);
|
||||||
|
|
||||||
|
// 创建Workbook
|
||||||
|
Workbook workbook = createWorkbook(inputStream, fileName);
|
||||||
|
|
||||||
|
// 获取Sheet
|
||||||
|
Sheet sheet = getSheet(workbook, sheetIndex, sheetName);
|
||||||
|
if (sheet == null) {
|
||||||
|
result.setSuccess(false);
|
||||||
|
result.setErrorMessage("未找到指定的Sheet");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析表头
|
||||||
|
Map<String, Integer> headerMap = parseHeader(sheet, headerRowIndex);
|
||||||
|
|
||||||
|
// 读取数据
|
||||||
|
readData(sheet, headerMap, targetClass, columnMappings, dataStartRowIndex,
|
||||||
|
skipEmptyRow, maxRows, continueOnError, result);
|
||||||
|
|
||||||
|
workbook.close();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.setSuccess(false);
|
||||||
|
result.setErrorMessage("读取Excel失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 创建Workbook对象
|
||||||
|
*/
|
||||||
|
private static Workbook createWorkbook(InputStream inputStream, String fileName) throws Exception {
|
||||||
|
if (fileName.endsWith(".xlsx")) {
|
||||||
|
return new XSSFWorkbook(inputStream);
|
||||||
|
} else if (fileName.endsWith(".xls")) {
|
||||||
|
return new HSSFWorkbook(inputStream);
|
||||||
|
} else {
|
||||||
|
throw new IllegalArgumentException("不支持的文件格式,仅支持.xls和.xlsx");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取Sheet
|
||||||
|
*/
|
||||||
|
private static Sheet getSheet(Workbook workbook, int sheetIndex, String sheetName) {
|
||||||
|
if (sheetName != null && !sheetName.isEmpty()) {
|
||||||
|
return workbook.getSheet(sheetName);
|
||||||
|
} else {
|
||||||
|
return workbook.getSheetAt(sheetIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 解析表头
|
||||||
|
*/
|
||||||
|
private static Map<String, Integer> parseHeader(Sheet sheet, int headerRowIndex) {
|
||||||
|
Map<String, Integer> headerMap = new HashMap<>();
|
||||||
|
Row headerRow = sheet.getRow(headerRowIndex);
|
||||||
|
|
||||||
|
if (headerRow != null) {
|
||||||
|
for (Cell cell : headerRow) {
|
||||||
|
String headerName = getCellValue(cell).toString().trim();
|
||||||
|
if (!headerName.isEmpty()) {
|
||||||
|
headerMap.put(headerName, cell.getColumnIndex());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return headerMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 读取数据
|
||||||
|
*/
|
||||||
|
private static void readData(Sheet sheet, Map<String, Integer> headerMap,
|
||||||
|
Class<?> targetClass, List<ExcelColumnMapping> columnMappings,
|
||||||
|
int startRow, boolean skipEmptyRow, int maxRows,
|
||||||
|
boolean continueOnError, ExcelReadResult<Object> result) {
|
||||||
|
int lastRowNum = sheet.getLastRowNum();
|
||||||
|
int endRow = maxRows > 0 ? Math.min(startRow + maxRows, lastRowNum + 1) : lastRowNum + 1;
|
||||||
|
|
||||||
|
for (int rowNum = startRow; rowNum < endRow; rowNum++) {
|
||||||
|
Row row = sheet.getRow(rowNum);
|
||||||
|
|
||||||
|
// 跳过空行
|
||||||
|
if (skipEmptyRow && isEmptyRow(row)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.setTotalRows(result.getTotalRows() + 1);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 将行数据转换为对象
|
||||||
|
Object data = convertRowToObject(row, headerMap, targetClass, columnMappings, rowNum + 1);
|
||||||
|
|
||||||
|
// 数据校验
|
||||||
|
List<ValidationParam> allValidations = new ArrayList<>();
|
||||||
|
for (ExcelColumnMapping mapping : columnMappings) {
|
||||||
|
if (!mapping.getValidationParams().isEmpty()) {
|
||||||
|
allValidations.addAll(mapping.getValidationParams());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!allValidations.isEmpty()) {
|
||||||
|
ValidationResult validationResult = ValidationUtils.validate(data, allValidations);
|
||||||
|
if (!validationResult.isValid()) {
|
||||||
|
result.addError(rowNum + 1, validationResult.getFirstError());
|
||||||
|
if (!continueOnError) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result.addData(data);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
result.addError(rowNum + 1, e.getMessage());
|
||||||
|
if (!continueOnError) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 判断是否为空行
|
||||||
|
*/
|
||||||
|
private static boolean isEmptyRow(Row row) {
|
||||||
|
if (row == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Cell cell : row) {
|
||||||
|
if (cell != null && cell.getCellType() != CellType.BLANK) {
|
||||||
|
String value = getCellValue(cell).toString().trim();
|
||||||
|
if (!value.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 将行数据转换为对象
|
||||||
|
*/
|
||||||
|
private static Object convertRowToObject(Row row, Map<String, Integer> headerMap,
|
||||||
|
Class<?> targetClass, List<ExcelColumnMapping> columnMappings,
|
||||||
|
int rowNum) throws Exception {
|
||||||
|
Object obj = targetClass.getDeclaredConstructor().newInstance();
|
||||||
|
|
||||||
|
for (ExcelColumnMapping mapping : columnMappings) {
|
||||||
|
// 获取单元格
|
||||||
|
Cell cell = getCell(row, mapping, headerMap);
|
||||||
|
|
||||||
|
// 获取单元格值
|
||||||
|
Object cellValue = getCellValue(cell);
|
||||||
|
|
||||||
|
// 处理空值
|
||||||
|
if (cellValue == null || cellValue.toString().trim().isEmpty()) {
|
||||||
|
if (mapping.isRequired()) {
|
||||||
|
throw new IllegalArgumentException("第" + rowNum + "行,字段[" +
|
||||||
|
(mapping.getColumnName() != null ? mapping.getColumnName() : "索引" + mapping.getColumnIndex())
|
||||||
|
+ "]不能为空");
|
||||||
|
}
|
||||||
|
// 使用默认值
|
||||||
|
if (mapping.getDefaultValue() != null && !mapping.getDefaultValue().isEmpty()) {
|
||||||
|
cellValue = mapping.getDefaultValue();
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 类型转换
|
||||||
|
Object fieldValue = convertValue(cellValue, mapping.getFieldType(), mapping.getDateFormat());
|
||||||
|
|
||||||
|
// 设置字段值
|
||||||
|
Field field = getField(targetClass, mapping.getFieldName());
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(obj, fieldValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取字段(支持父类)
|
||||||
|
*/
|
||||||
|
private static Field getField(Class<?> clazz, String fieldName) throws NoSuchFieldException {
|
||||||
|
try {
|
||||||
|
return clazz.getDeclaredField(fieldName);
|
||||||
|
} catch (NoSuchFieldException e) {
|
||||||
|
// 尝试从父类获取
|
||||||
|
Class<?> superClass = clazz.getSuperclass();
|
||||||
|
if (superClass != null) {
|
||||||
|
return getField(superClass, fieldName);
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取单元格
|
||||||
|
*/
|
||||||
|
private static Cell getCell(Row row, ExcelColumnMapping mapping, Map<String, Integer> headerMap) {
|
||||||
|
if (row == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 优先使用索引
|
||||||
|
if (mapping.getColumnIndex() != null) {
|
||||||
|
return row.getCell(mapping.getColumnIndex());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用列名
|
||||||
|
Integer columnIndex = headerMap.get(mapping.getColumnName());
|
||||||
|
if (columnIndex != null) {
|
||||||
|
return row.getCell(columnIndex);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 获取单元格值
|
||||||
|
*/
|
||||||
|
private static Object getCellValue(Cell cell) {
|
||||||
|
if (cell == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (cell.getCellType()) {
|
||||||
|
case STRING:
|
||||||
|
return cell.getStringCellValue();
|
||||||
|
case NUMERIC:
|
||||||
|
if (DateUtil.isCellDateFormatted(cell)) {
|
||||||
|
return cell.getDateCellValue();
|
||||||
|
} else {
|
||||||
|
return cell.getNumericCellValue();
|
||||||
|
}
|
||||||
|
case BOOLEAN:
|
||||||
|
return cell.getBooleanCellValue();
|
||||||
|
case FORMULA:
|
||||||
|
return cell.getCellFormula();
|
||||||
|
case BLANK:
|
||||||
|
return "";
|
||||||
|
default:
|
||||||
|
return cell.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 类型转换
|
||||||
|
*/
|
||||||
|
private static Object convertValue(Object value, Class<?> targetType, String dateFormat) throws Exception {
|
||||||
|
if (value == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String strValue = value.toString().trim();
|
||||||
|
|
||||||
|
// String类型
|
||||||
|
if (targetType == String.class) {
|
||||||
|
return strValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Integer类型
|
||||||
|
if (targetType == Integer.class || targetType == int.class) {
|
||||||
|
if (value instanceof Number) {
|
||||||
|
return ((Number) value).intValue();
|
||||||
|
}
|
||||||
|
return Integer.parseInt(strValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Long类型
|
||||||
|
if (targetType == Long.class || targetType == long.class) {
|
||||||
|
if (value instanceof Number) {
|
||||||
|
return ((Number) value).longValue();
|
||||||
|
}
|
||||||
|
return Long.parseLong(strValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Double类型
|
||||||
|
if (targetType == Double.class || targetType == double.class) {
|
||||||
|
if (value instanceof Number) {
|
||||||
|
return ((Number) value).doubleValue();
|
||||||
|
}
|
||||||
|
return Double.parseDouble(strValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float类型
|
||||||
|
if (targetType == Float.class || targetType == float.class) {
|
||||||
|
if (value instanceof Number) {
|
||||||
|
return ((Number) value).floatValue();
|
||||||
|
}
|
||||||
|
return Float.parseFloat(strValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Boolean类型
|
||||||
|
if (targetType == Boolean.class || targetType == boolean.class) {
|
||||||
|
if (value instanceof Boolean) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
return Boolean.parseBoolean(strValue) || "1".equals(strValue) ||
|
||||||
|
"是".equals(strValue) || "true".equalsIgnoreCase(strValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Date类型
|
||||||
|
if (targetType == Date.class) {
|
||||||
|
if (value instanceof Date) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
|
||||||
|
return sdf.parse(strValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 其他类型
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,429 @@
|
|||||||
|
package org.xyzh.common.utils.excel;
|
||||||
|
|
||||||
|
import org.xyzh.common.utils.validation.ValidationParam;
|
||||||
|
import org.xyzh.common.utils.validation.method.ValidateMethodType;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description Excel工具使用示例
|
||||||
|
* @filename ExcelUtilsExample.java
|
||||||
|
* @author yslg
|
||||||
|
* @copyright xyzh
|
||||||
|
* @since 2025-10-16
|
||||||
|
*/
|
||||||
|
public class ExcelUtilsExample {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 示例实体类:用户信息
|
||||||
|
*/
|
||||||
|
public static class UserInfo {
|
||||||
|
private String name;
|
||||||
|
private Integer age;
|
||||||
|
private String phone;
|
||||||
|
private String email;
|
||||||
|
private String idCard;
|
||||||
|
private Date joinDate;
|
||||||
|
private Boolean active;
|
||||||
|
|
||||||
|
// Getters and Setters
|
||||||
|
public String getName() { return name; }
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
|
||||||
|
public Integer getAge() { return age; }
|
||||||
|
public void setAge(Integer age) { this.age = age; }
|
||||||
|
|
||||||
|
public String getPhone() { return phone; }
|
||||||
|
public void setPhone(String phone) { this.phone = phone; }
|
||||||
|
|
||||||
|
public String getEmail() { return email; }
|
||||||
|
public void setEmail(String email) { this.email = email; }
|
||||||
|
|
||||||
|
public String getIdCard() { return idCard; }
|
||||||
|
public void setIdCard(String idCard) { this.idCard = idCard; }
|
||||||
|
|
||||||
|
public Date getJoinDate() { return joinDate; }
|
||||||
|
public void setJoinDate(Date joinDate) { this.joinDate = joinDate; }
|
||||||
|
|
||||||
|
public Boolean getActive() { return active; }
|
||||||
|
public void setActive(Boolean active) { this.active = active; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "UserInfo{" +
|
||||||
|
"name='" + name + '\'' +
|
||||||
|
", age=" + age +
|
||||||
|
", phone='" + phone + '\'' +
|
||||||
|
", email='" + email + '\'' +
|
||||||
|
", idCard='" + idCard + '\'' +
|
||||||
|
", joinDate=" + joinDate +
|
||||||
|
", active=" + active +
|
||||||
|
'}';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 示例1:基本使用
|
||||||
|
*/
|
||||||
|
public static void example1_BasicUsage() {
|
||||||
|
System.out.println("========== 示例1: 基本使用 ==========");
|
||||||
|
|
||||||
|
// 1. 定义列映射关系
|
||||||
|
List<ExcelColumnMapping> columnMappings = Arrays.asList(
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("姓名")
|
||||||
|
.fieldName("name")
|
||||||
|
.fieldType(String.class)
|
||||||
|
.required()
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("年龄")
|
||||||
|
.fieldName("age")
|
||||||
|
.fieldType(Integer.class)
|
||||||
|
.required()
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("手机号")
|
||||||
|
.fieldName("phone")
|
||||||
|
.fieldType(String.class)
|
||||||
|
.required()
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("邮箱")
|
||||||
|
.fieldName("email")
|
||||||
|
.fieldType(String.class)
|
||||||
|
.validations(Arrays.asList(
|
||||||
|
ValidationParam.builder()
|
||||||
|
.fieldName("email")
|
||||||
|
.fieldLabel("邮箱")
|
||||||
|
.required()
|
||||||
|
.validateMethod(ValidateMethodType.EMAIL)
|
||||||
|
.build()
|
||||||
|
))
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("入职日期")
|
||||||
|
.fieldName("joinDate")
|
||||||
|
.fieldType(Date.class)
|
||||||
|
.dateFormat("yyyy-MM-dd")
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("是否在职")
|
||||||
|
.fieldName("active")
|
||||||
|
.fieldType(Boolean.class)
|
||||||
|
.defaultValue("true")
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. 读取Excel
|
||||||
|
File file = new File("users.xlsx");
|
||||||
|
ExcelReadResult<Object> result = ExcelReaderUtils.readExcel(
|
||||||
|
file,
|
||||||
|
UserInfo.class,
|
||||||
|
columnMappings
|
||||||
|
);
|
||||||
|
|
||||||
|
// 3. 处理结果
|
||||||
|
if (result.isSuccess()) {
|
||||||
|
System.out.println("读取成功!");
|
||||||
|
System.out.println("总行数: " + result.getTotalRows());
|
||||||
|
System.out.println("成功行数: " + result.getSuccessRows());
|
||||||
|
|
||||||
|
for (Object obj : result.getDataList()) {
|
||||||
|
UserInfo user = (UserInfo) obj;
|
||||||
|
System.out.println(user);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
System.out.println("读取失败: " + result.getErrorMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 示例2:带数据校验
|
||||||
|
*/
|
||||||
|
public static void example2_WithValidation() {
|
||||||
|
System.out.println("\n========== 示例2: 带数据校验 ==========");
|
||||||
|
|
||||||
|
// 定义列映射关系(带校验)
|
||||||
|
List<ExcelColumnMapping> columnMappings = Arrays.asList(
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("姓名")
|
||||||
|
.fieldName("name")
|
||||||
|
.fieldType(String.class)
|
||||||
|
.required()
|
||||||
|
.addValidation(
|
||||||
|
ValidationParam.builder()
|
||||||
|
.fieldName("name")
|
||||||
|
.fieldLabel("姓名")
|
||||||
|
.required()
|
||||||
|
.minLength(2)
|
||||||
|
.maxLength(20)
|
||||||
|
.validateMethod(ValidateMethodType.CHINESE)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("年龄")
|
||||||
|
.fieldName("age")
|
||||||
|
.fieldType(Integer.class)
|
||||||
|
.required()
|
||||||
|
.addValidation(
|
||||||
|
ValidationParam.builder()
|
||||||
|
.fieldName("age")
|
||||||
|
.fieldLabel("年龄")
|
||||||
|
.required()
|
||||||
|
.customValidator(value -> {
|
||||||
|
Integer age = (Integer) value;
|
||||||
|
return age >= 18 && age <= 65;
|
||||||
|
})
|
||||||
|
.customErrorMessage("年龄必须在18-65岁之间")
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("手机号")
|
||||||
|
.fieldName("phone")
|
||||||
|
.fieldType(String.class)
|
||||||
|
.required()
|
||||||
|
.addValidation(
|
||||||
|
ValidationParam.builder()
|
||||||
|
.fieldName("phone")
|
||||||
|
.fieldLabel("手机号")
|
||||||
|
.required()
|
||||||
|
.validateMethod(ValidateMethodType.PHONE)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("邮箱")
|
||||||
|
.fieldName("email")
|
||||||
|
.fieldType(String.class)
|
||||||
|
.addValidation(
|
||||||
|
ValidationParam.builder()
|
||||||
|
.fieldName("email")
|
||||||
|
.fieldLabel("邮箱")
|
||||||
|
.required(false)
|
||||||
|
.validateMethod(ValidateMethodType.EMAIL)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("身份证号")
|
||||||
|
.fieldName("idCard")
|
||||||
|
.fieldType(String.class)
|
||||||
|
.addValidation(
|
||||||
|
ValidationParam.builder()
|
||||||
|
.fieldName("idCard")
|
||||||
|
.fieldLabel("身份证号")
|
||||||
|
.required(false)
|
||||||
|
.validateMethod(ValidateMethodType.ID_CARD)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
// 读取配置
|
||||||
|
Map<String, Object> options = new HashMap<>();
|
||||||
|
options.put("continueOnError", true); // 遇到错误继续读取
|
||||||
|
|
||||||
|
File file = new File("users.xlsx");
|
||||||
|
ExcelReadResult<Object> result = ExcelReaderUtils.readExcel(
|
||||||
|
file,
|
||||||
|
UserInfo.class,
|
||||||
|
columnMappings,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
|
// 处理结果
|
||||||
|
System.out.println("读取完成!");
|
||||||
|
System.out.println("总行数: " + result.getTotalRows());
|
||||||
|
System.out.println("成功行数: " + result.getSuccessRows());
|
||||||
|
System.out.println("失败行数: " + result.getErrorRowsCount());
|
||||||
|
|
||||||
|
// 显示错误信息
|
||||||
|
if (result.hasErrors()) {
|
||||||
|
System.out.println("\n错误信息:");
|
||||||
|
result.getErrorRows().forEach((rowNum, error) -> {
|
||||||
|
System.out.println("第" + rowNum + "行: " + error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 示例3:使用列索引
|
||||||
|
*/
|
||||||
|
public static void example3_UseColumnIndex() {
|
||||||
|
System.out.println("\n========== 示例3: 使用列索引 ==========");
|
||||||
|
|
||||||
|
// 使用列索引而非列名
|
||||||
|
List<ExcelColumnMapping> columnMappings = Arrays.asList(
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnIndex(0) // 第1列
|
||||||
|
.fieldName("name")
|
||||||
|
.fieldType(String.class)
|
||||||
|
.required()
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnIndex(1) // 第2列
|
||||||
|
.fieldName("age")
|
||||||
|
.fieldType(Integer.class)
|
||||||
|
.required()
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnIndex(2) // 第3列
|
||||||
|
.fieldName("phone")
|
||||||
|
.fieldType(String.class)
|
||||||
|
.required()
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
File file = new File("users.xlsx");
|
||||||
|
ExcelReadResult<Object> result = ExcelReaderUtils.readExcel(
|
||||||
|
file,
|
||||||
|
UserInfo.class,
|
||||||
|
columnMappings
|
||||||
|
);
|
||||||
|
|
||||||
|
System.out.println("成功读取: " + result.getSuccessRows() + " 行");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 示例4:自定义配置
|
||||||
|
*/
|
||||||
|
public static void example4_CustomConfig() {
|
||||||
|
System.out.println("\n========== 示例4: 自定义配置 ==========");
|
||||||
|
|
||||||
|
List<ExcelColumnMapping> columnMappings = Arrays.asList(
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("姓名")
|
||||||
|
.fieldName("name")
|
||||||
|
.fieldType(String.class)
|
||||||
|
.required()
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("年龄")
|
||||||
|
.fieldName("age")
|
||||||
|
.fieldType(Integer.class)
|
||||||
|
.required()
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
// 自定义配置
|
||||||
|
Map<String, Object> options = new HashMap<>();
|
||||||
|
options.put("sheetName", "员工信息"); // 指定Sheet名称
|
||||||
|
options.put("headerRowIndex", 0); // 表头在第1行
|
||||||
|
options.put("dataStartRowIndex", 1); // 数据从第2行开始
|
||||||
|
options.put("skipEmptyRow", true); // 跳过空行
|
||||||
|
options.put("maxRows", 1000); // 最多读取1000行
|
||||||
|
options.put("continueOnError", true); // 遇到错误继续
|
||||||
|
|
||||||
|
File file = new File("users.xlsx");
|
||||||
|
ExcelReadResult<Object> result = ExcelReaderUtils.readExcel(
|
||||||
|
file,
|
||||||
|
UserInfo.class,
|
||||||
|
columnMappings,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
|
System.out.println("读取结果: " + result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 示例5:在Controller中使用
|
||||||
|
*/
|
||||||
|
public static void example5_InController() {
|
||||||
|
System.out.println("\n========== 示例5: 在Controller中使用(代码示例)==========");
|
||||||
|
System.out.println("""
|
||||||
|
@PostMapping("/import")
|
||||||
|
public ResultDomain<String> importUsers(@RequestParam("file") MultipartFile file) {
|
||||||
|
try {
|
||||||
|
// 1. 定义列映射关系
|
||||||
|
List<ExcelColumnMapping> columnMappings = Arrays.asList(
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("姓名")
|
||||||
|
.fieldName("name")
|
||||||
|
.fieldType(String.class)
|
||||||
|
.required()
|
||||||
|
.addValidation(
|
||||||
|
ValidationParam.builder()
|
||||||
|
.fieldName("name")
|
||||||
|
.fieldLabel("姓名")
|
||||||
|
.required()
|
||||||
|
.validateMethod(ValidateMethodType.CHINESE)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("手机号")
|
||||||
|
.fieldName("phone")
|
||||||
|
.fieldType(String.class)
|
||||||
|
.required()
|
||||||
|
.addValidation(
|
||||||
|
ValidationParam.builder()
|
||||||
|
.fieldName("phone")
|
||||||
|
.fieldLabel("手机号")
|
||||||
|
.required()
|
||||||
|
.validateMethod(ValidateMethodType.PHONE)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. 读取Excel
|
||||||
|
ExcelReadResult<Object> result = ExcelReaderUtils.readExcel(
|
||||||
|
file.getInputStream(),
|
||||||
|
file.getOriginalFilename(),
|
||||||
|
UserInfo.class,
|
||||||
|
columnMappings
|
||||||
|
);
|
||||||
|
|
||||||
|
// 3. 处理结果
|
||||||
|
if (result.hasErrors()) {
|
||||||
|
StringBuilder errorMsg = new StringBuilder();
|
||||||
|
errorMsg.append("导入失败,共").append(result.getErrorRowsCount()).append("行数据有误:\\n");
|
||||||
|
result.getErrorRows().forEach((rowNum, error) -> {
|
||||||
|
errorMsg.append("第").append(rowNum).append("行: ").append(error).append("\\n");
|
||||||
|
});
|
||||||
|
return ResultDomain.fail(errorMsg.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 保存数据
|
||||||
|
List<UserInfo> users = new ArrayList<>();
|
||||||
|
for (Object obj : result.getDataList()) {
|
||||||
|
users.add((UserInfo) obj);
|
||||||
|
}
|
||||||
|
userService.batchSave(users);
|
||||||
|
|
||||||
|
return ResultDomain.success("导入成功,共导入" + result.getSuccessRows() + "条数据");
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
return ResultDomain.fail("导入失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// 运行示例
|
||||||
|
// example1_BasicUsage();
|
||||||
|
// example2_WithValidation();
|
||||||
|
// example3_UseColumnIndex();
|
||||||
|
// example4_CustomConfig();
|
||||||
|
example5_InController();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,542 @@
|
|||||||
|
# Excel读取工具使用说明
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
这是一个功能强大的Excel读取工具,通过配置 **Excel列对象** 来定义Excel列与Java对象字段的映射关系,支持自动构建对象并集成数据校验功能。
|
||||||
|
|
||||||
|
## 核心组件
|
||||||
|
|
||||||
|
### 1. ExcelColumnMapping - Excel列映射配置
|
||||||
|
定义Excel列与对象字段的映射关系:
|
||||||
|
- **columnName** - Excel列名(表头名称)
|
||||||
|
- **columnIndex** - Excel列索引(从0开始,优先级高于列名)
|
||||||
|
- **fieldName** - 对象字段名
|
||||||
|
- **fieldType** - 字段类型
|
||||||
|
- **required** - 是否必填
|
||||||
|
- **defaultValue** - 默认值
|
||||||
|
- **dateFormat** - 日期格式
|
||||||
|
- **validationParams** - 校验参数列表
|
||||||
|
|
||||||
|
### 2. ExcelReaderUtils - Excel读取工具类
|
||||||
|
提供静态方法读取Excel:
|
||||||
|
- `readExcel(File, Class, List<ExcelColumnMapping>)` - 从文件读取
|
||||||
|
- `readExcel(File, Class, List<ExcelColumnMapping>, Map<options>)` - 从文件读取(带配置)
|
||||||
|
- `readExcel(InputStream, fileName, Class, List<ExcelColumnMapping>)` - 从输入流读取
|
||||||
|
- `readExcel(InputStream, fileName, Class, List<ExcelColumnMapping>, Map<options>)` - 从输入流读取(带配置)
|
||||||
|
|
||||||
|
### 3. ExcelReadResult - 读取结果
|
||||||
|
包含读取结果的详细信息:
|
||||||
|
- 成功数据列表
|
||||||
|
- 错误行信息(行号 -> 错误消息)
|
||||||
|
- 统计信息(总行数、成功数、失败数)
|
||||||
|
|
||||||
|
## 基本使用
|
||||||
|
|
||||||
|
### 1. 定义实体类
|
||||||
|
|
||||||
|
```java
|
||||||
|
public class UserInfo {
|
||||||
|
private String name;
|
||||||
|
private Integer age;
|
||||||
|
private String phone;
|
||||||
|
private String email;
|
||||||
|
private Date joinDate;
|
||||||
|
private Boolean active;
|
||||||
|
|
||||||
|
// Getters and Setters...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 定义列映射关系
|
||||||
|
|
||||||
|
```java
|
||||||
|
List<ExcelColumnMapping> columnMappings = Arrays.asList(
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("姓名") // Excel列名
|
||||||
|
.fieldName("name") // 对象字段名
|
||||||
|
.fieldType(String.class) // 字段类型
|
||||||
|
.required() // 必填
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("年龄")
|
||||||
|
.fieldName("age")
|
||||||
|
.fieldType(Integer.class)
|
||||||
|
.required()
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("手机号")
|
||||||
|
.fieldName("phone")
|
||||||
|
.fieldType(String.class)
|
||||||
|
.required()
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("邮箱")
|
||||||
|
.fieldName("email")
|
||||||
|
.fieldType(String.class)
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("入职日期")
|
||||||
|
.fieldName("joinDate")
|
||||||
|
.fieldType(Date.class)
|
||||||
|
.dateFormat("yyyy-MM-dd")
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("是否在职")
|
||||||
|
.fieldName("active")
|
||||||
|
.fieldType(Boolean.class)
|
||||||
|
.defaultValue("true")
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 读取Excel
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 读取文件
|
||||||
|
File file = new File("users.xlsx");
|
||||||
|
ExcelReadResult<Object> result = ExcelReaderUtils.readExcel(
|
||||||
|
file,
|
||||||
|
UserInfo.class,
|
||||||
|
columnMappings
|
||||||
|
);
|
||||||
|
|
||||||
|
// 处理结果
|
||||||
|
if (result.isSuccess()) {
|
||||||
|
for (Object obj : result.getDataList()) {
|
||||||
|
UserInfo user = (UserInfo) obj;
|
||||||
|
System.out.println(user);
|
||||||
|
}
|
||||||
|
System.out.println("成功读取: " + result.getSuccessRows() + " 行");
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 带数据校验的读取
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 定义列映射关系(带校验)
|
||||||
|
List<ExcelColumnMapping> columnMappings = Arrays.asList(
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("姓名")
|
||||||
|
.fieldName("name")
|
||||||
|
.fieldType(String.class)
|
||||||
|
.required()
|
||||||
|
.addValidation(
|
||||||
|
ValidationParam.builder()
|
||||||
|
.fieldName("name")
|
||||||
|
.fieldLabel("姓名")
|
||||||
|
.required()
|
||||||
|
.minLength(2)
|
||||||
|
.maxLength(20)
|
||||||
|
.validateMethod(ValidateMethodType.CHINESE)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("年龄")
|
||||||
|
.fieldName("age")
|
||||||
|
.fieldType(Integer.class)
|
||||||
|
.required()
|
||||||
|
.addValidation(
|
||||||
|
ValidationParam.builder()
|
||||||
|
.fieldName("age")
|
||||||
|
.fieldLabel("年龄")
|
||||||
|
.required()
|
||||||
|
.customValidator(value -> {
|
||||||
|
Integer age = (Integer) value;
|
||||||
|
return age >= 18 && age <= 65;
|
||||||
|
})
|
||||||
|
.customErrorMessage("年龄必须在18-65岁之间")
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("手机号")
|
||||||
|
.fieldName("phone")
|
||||||
|
.fieldType(String.class)
|
||||||
|
.required()
|
||||||
|
.addValidation(
|
||||||
|
ValidationParam.builder()
|
||||||
|
.fieldName("phone")
|
||||||
|
.fieldLabel("手机号")
|
||||||
|
.required()
|
||||||
|
.validateMethod(ValidateMethodType.PHONE)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("邮箱")
|
||||||
|
.fieldName("email")
|
||||||
|
.fieldType(String.class)
|
||||||
|
.addValidation(
|
||||||
|
ValidationParam.builder()
|
||||||
|
.fieldName("email")
|
||||||
|
.fieldLabel("邮箱")
|
||||||
|
.required(false)
|
||||||
|
.validateMethod(ValidateMethodType.EMAIL)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
// 配置选项
|
||||||
|
Map<String, Object> options = new HashMap<>();
|
||||||
|
options.put("continueOnError", true); // 遇到错误继续读取
|
||||||
|
|
||||||
|
// 读取Excel
|
||||||
|
File file = new File("users.xlsx");
|
||||||
|
ExcelReadResult<Object> result = ExcelReaderUtils.readExcel(
|
||||||
|
file,
|
||||||
|
UserInfo.class,
|
||||||
|
columnMappings,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
|
||||||
|
// 处理结果
|
||||||
|
System.out.println("总行数: " + result.getTotalRows());
|
||||||
|
System.out.println("成功: " + result.getSuccessRows());
|
||||||
|
System.out.println("失败: " + result.getErrorRowsCount());
|
||||||
|
|
||||||
|
// 显示错误
|
||||||
|
if (result.hasErrors()) {
|
||||||
|
result.getErrorRows().forEach((rowNum, error) -> {
|
||||||
|
System.out.println("第" + rowNum + "行: " + error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. 使用列索引而非列名
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 使用列索引(从0开始)
|
||||||
|
List<ExcelColumnMapping> columnMappings = Arrays.asList(
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnIndex(0) // 第1列
|
||||||
|
.fieldName("name")
|
||||||
|
.fieldType(String.class)
|
||||||
|
.required()
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnIndex(1) // 第2列
|
||||||
|
.fieldName("age")
|
||||||
|
.fieldType(Integer.class)
|
||||||
|
.required()
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnIndex(2) // 第3列
|
||||||
|
.fieldName("phone")
|
||||||
|
.fieldType(String.class)
|
||||||
|
.required()
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
File file = new File("users.xlsx");
|
||||||
|
ExcelReadResult<Object> result = ExcelReaderUtils.readExcel(
|
||||||
|
file,
|
||||||
|
UserInfo.class,
|
||||||
|
columnMappings
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### 6. 自定义配置选项
|
||||||
|
|
||||||
|
以下是旧版本的校验代码,现在已经整合到ExcelColumnMapping中:
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 旧版本(已弃用)
|
||||||
|
List<ValidationParam> validationParams = Arrays.asList(
|
||||||
|
ValidationParam.builder()
|
||||||
|
.fieldName("name")
|
||||||
|
.fieldLabel("姓名")
|
||||||
|
.required()
|
||||||
|
.minLength(2)
|
||||||
|
.maxLength(20)
|
||||||
|
.validateMethod(ValidateMethodType.CHINESE)
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ValidationParam.builder()
|
||||||
|
.fieldName("phone")
|
||||||
|
.fieldLabel("手机号")
|
||||||
|
.required()
|
||||||
|
.validateMethod(ValidateMethodType.PHONE)
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ValidationParam.builder()
|
||||||
|
.fieldName("email")
|
||||||
|
.fieldLabel("邮箱")
|
||||||
|
.required(false)
|
||||||
|
.validateMethod(ValidateMethodType.EMAIL)
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ValidationParam.builder()
|
||||||
|
.fieldName("age")
|
||||||
|
.fieldLabel("年龄")
|
||||||
|
.required()
|
||||||
|
.customValidator(value -> {
|
||||||
|
Integer age = (Integer) value;
|
||||||
|
return age >= 18 && age <= 65;
|
||||||
|
})
|
||||||
|
.customErrorMessage("年龄必须在18-65岁之间")
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
// 创建配置
|
||||||
|
ExcelReaderConfig<UserInfo> config = new ExcelReaderConfig<>(UserInfo.class)
|
||||||
|
.setValidationParams(validationParams)
|
||||||
|
.setContinueOnError(true); // 遇到错误继续读取
|
||||||
|
|
||||||
|
// 读取
|
||||||
|
ExcelReader<UserInfo> reader = ExcelReader.create(config);
|
||||||
|
ExcelReadResult<UserInfo> result = reader.read(file);
|
||||||
|
|
||||||
|
// 处理结果
|
||||||
|
System.out.println("总行数: " + result.getTotalRows());
|
||||||
|
System.out.println("成功: " + result.getSuccessRows());
|
||||||
|
System.out.println("失败: " + result.getErrorRowsCount());
|
||||||
|
|
||||||
|
// 显示错误
|
||||||
|
if (result.hasErrors()) {
|
||||||
|
result.getErrorRows().forEach((rowNum, error) -> {
|
||||||
|
System.out.println("第" + rowNum + "行: " + error);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```java
|
||||||
|
List<ExcelColumnMapping> columnMappings = Arrays.asList(
|
||||||
|
// 列映射配置...
|
||||||
|
);
|
||||||
|
|
||||||
|
// 自定义配置选项
|
||||||
|
Map<String, Object> options = new HashMap<>();
|
||||||
|
options.put("sheetName", "员工信息"); // 指定Sheet名称
|
||||||
|
options.put("headerRowIndex", 0); // 表头在第1行
|
||||||
|
options.put("dataStartRowIndex", 1); // 数据从第2行开始
|
||||||
|
options.put("skipEmptyRow", true); // 跳过空行
|
||||||
|
options.put("maxRows", 1000); // 最多读取1000行
|
||||||
|
options.put("continueOnError", true); // 遇到错误继续
|
||||||
|
|
||||||
|
File file = new File("users.xlsx");
|
||||||
|
ExcelReadResult<Object> result = ExcelReaderUtils.readExcel(
|
||||||
|
file,
|
||||||
|
UserInfo.class,
|
||||||
|
columnMappings,
|
||||||
|
options
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
## 在Controller中使用
|
||||||
|
|
||||||
|
```java
|
||||||
|
@PostMapping("/import")
|
||||||
|
public ResultDomain<String> importUsers(@RequestParam("file") MultipartFile file) {
|
||||||
|
try {
|
||||||
|
// 1. 定义列映射关系(带校验)
|
||||||
|
List<ExcelColumnMapping> columnMappings = Arrays.asList(
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("姓名")
|
||||||
|
.fieldName("name")
|
||||||
|
.fieldType(String.class)
|
||||||
|
.required()
|
||||||
|
.addValidation(
|
||||||
|
ValidationParam.builder()
|
||||||
|
.fieldName("name")
|
||||||
|
.fieldLabel("姓名")
|
||||||
|
.required()
|
||||||
|
.validateMethod(ValidateMethodType.CHINESE)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("手机号")
|
||||||
|
.fieldName("phone")
|
||||||
|
.fieldType(String.class)
|
||||||
|
.required()
|
||||||
|
.addValidation(
|
||||||
|
ValidationParam.builder()
|
||||||
|
.fieldName("phone")
|
||||||
|
.fieldLabel("手机号")
|
||||||
|
.required()
|
||||||
|
.validateMethod(ValidateMethodType.PHONE)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build(),
|
||||||
|
|
||||||
|
ExcelColumnMapping.builder()
|
||||||
|
.columnName("邮箱")
|
||||||
|
.fieldName("email")
|
||||||
|
.fieldType(String.class)
|
||||||
|
.addValidation(
|
||||||
|
ValidationParam.builder()
|
||||||
|
.fieldName("email")
|
||||||
|
.fieldLabel("邮箱")
|
||||||
|
.validateMethod(ValidateMethodType.EMAIL)
|
||||||
|
.build()
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. 读取Excel
|
||||||
|
ExcelReadResult<Object> result = ExcelReaderUtils.readExcel(
|
||||||
|
file.getInputStream(),
|
||||||
|
file.getOriginalFilename(),
|
||||||
|
UserInfo.class,
|
||||||
|
columnMappings
|
||||||
|
);
|
||||||
|
|
||||||
|
// 3. 处理结果
|
||||||
|
if (result.hasErrors()) {
|
||||||
|
StringBuilder errorMsg = new StringBuilder();
|
||||||
|
errorMsg.append("导入失败,共").append(result.getErrorRowsCount()).append("行数据有误:\n");
|
||||||
|
result.getErrorRows().forEach((rowNum, error) -> {
|
||||||
|
errorMsg.append("第").append(rowNum).append("行: ").append(error).append("\n");
|
||||||
|
});
|
||||||
|
return ResultDomain.fail(errorMsg.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 保存数据
|
||||||
|
List<UserInfo> users = new ArrayList<>();
|
||||||
|
for (Object obj : result.getDataList()) {
|
||||||
|
users.add((UserInfo) obj);
|
||||||
|
}
|
||||||
|
userService.batchSave(users);
|
||||||
|
|
||||||
|
return ResultDomain.success("导入成功,共导入" + result.getSuccessRows() + "条数据");
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
return ResultDomain.fail("导入失败: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 支持的数据类型
|
||||||
|
|
||||||
|
- **String** - 字符串
|
||||||
|
- **Integer/int** - 整数
|
||||||
|
- **Long/long** - 长整数
|
||||||
|
- **Double/double** - 双精度浮点数
|
||||||
|
- **Float/float** - 单精度浮点数
|
||||||
|
- **Boolean/boolean** - 布尔值(支持:true/false、1/0、是/否)
|
||||||
|
- **Date** - 日期(需指定dateFormat)
|
||||||
|
|
||||||
|
## 配置选项
|
||||||
|
|
||||||
|
### ExcelColumnMapping Builder方法
|
||||||
|
|
||||||
|
| 方法 | 说明 | 必填 |
|
||||||
|
|------|------|------|
|
||||||
|
| columnName(String) | 设置Excel列名 | columnName和columnIndex至少一个 |
|
||||||
|
| columnIndex(int) | 设置Excel列索引(从0开始,优先级高于columnName) | columnName和columnIndex至少一个 |
|
||||||
|
| fieldName(String) | 设置对象字段名 | 是 |
|
||||||
|
| fieldType(Class<?>) | 设置字段类型 | 否(默认String.class) |
|
||||||
|
| required() / required(boolean) | 设置是否必填 | 否(默认false) |
|
||||||
|
| defaultValue(String) | 设置默认值 | 否 |
|
||||||
|
| dateFormat(String) | 设置日期格式 | 否(默认"yyyy-MM-dd") |
|
||||||
|
| addValidation(ValidationParam) | 添加校验参数 | 否 |
|
||||||
|
| validations(List<ValidationParam>) | 设置校验参数列表 | 否 |
|
||||||
|
|
||||||
|
### Options配置项
|
||||||
|
|
||||||
|
传递给 `readExcel` 方法的 `Map<String, Object> options` 支持以下选项:
|
||||||
|
|
||||||
|
| 键名 | 类型 | 说明 | 默认值 |
|
||||||
|
|------|------|------|--------|
|
||||||
|
| headerRowIndex | int | 表头所在行(从0开始) | 0 |
|
||||||
|
| dataStartRowIndex | int | 数据起始行(从0开始) | 1 |
|
||||||
|
| sheetIndex | int | Sheet索引(从0开始) | 0 |
|
||||||
|
| sheetName | String | Sheet名称(优先级高于sheetIndex) | null |
|
||||||
|
| skipEmptyRow | boolean | 跳过空行 | true |
|
||||||
|
| maxRows | int | 最大读取行数(0=不限制) | 0 |
|
||||||
|
| continueOnError | boolean | 遇到错误继续读取 | true |
|
||||||
|
|
||||||
|
## 核心特性
|
||||||
|
|
||||||
|
1. **配置驱动**:通过ExcelColumnMapping配置Excel列与对象字段的映射关系
|
||||||
|
2. **无需注解**:不需要在实体类上添加注解,更加灵活
|
||||||
|
3. **数据校验**:集成ValidationUtils和ValidateMethodType进行专业数据校验
|
||||||
|
4. **灵活配置**:支持多种配置选项(Sheet选择、行范围、错误处理等)
|
||||||
|
5. **错误处理**:详细的错误信息和错误行记录
|
||||||
|
6. **类型转换**:自动进行类型转换
|
||||||
|
7. **空值处理**:支持默认值和空值校验
|
||||||
|
8. **多Sheet支持**:可指定Sheet名称或索引
|
||||||
|
9. **两种映射方式**:支持列名和列索引两种方式
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **实体类要求**:必须有无参构造函数
|
||||||
|
2. **字段访问**:字段必须有setter方法或可访问(支持父类字段)
|
||||||
|
3. **列映射优先级**:columnIndex优先级高于columnName
|
||||||
|
4. **日期类型**:必须指定dateFormat
|
||||||
|
5. **布尔类型**:支持多种格式(true/false、1/0、是/否)
|
||||||
|
6. **错误处理**:根据continueOnError选项决定遇到错误时是否继续
|
||||||
|
7. **文件格式**:支持.xls和.xlsx两种格式
|
||||||
|
8. **类型转换**:自动转换支持String、Integer、Long、Double、Float、Boolean、Date
|
||||||
|
|
||||||
|
## 依赖
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.poi</groupId>
|
||||||
|
<artifactId>poi</artifactId>
|
||||||
|
<version>5.2.3</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.poi</groupId>
|
||||||
|
<artifactId>poi-ooxml</artifactId>
|
||||||
|
<version>5.2.3</version>
|
||||||
|
</dependency>
|
||||||
|
```
|
||||||
|
|
||||||
|
## API说明
|
||||||
|
|
||||||
|
### ExcelReaderUtils 静态方法
|
||||||
|
|
||||||
|
```java
|
||||||
|
// 从文件读取(基本)
|
||||||
|
public static ExcelReadResult<Object> readExcel(
|
||||||
|
File file,
|
||||||
|
Class<?> targetClass,
|
||||||
|
List<ExcelColumnMapping> columnMappings
|
||||||
|
)
|
||||||
|
|
||||||
|
// 从文件读取(带配置)
|
||||||
|
public static ExcelReadResult<Object> readExcel(
|
||||||
|
File file,
|
||||||
|
Class<?> targetClass,
|
||||||
|
List<ExcelColumnMapping> columnMappings,
|
||||||
|
Map<String, Object> options
|
||||||
|
)
|
||||||
|
|
||||||
|
// 从输入流读取(基本)
|
||||||
|
public static ExcelReadResult<Object> readExcel(
|
||||||
|
InputStream inputStream,
|
||||||
|
String fileName,
|
||||||
|
Class<?> targetClass,
|
||||||
|
List<ExcelColumnMapping> columnMappings
|
||||||
|
)
|
||||||
|
|
||||||
|
// 从输入流读取(带配置)
|
||||||
|
public static ExcelReadResult<Object> readExcel(
|
||||||
|
InputStream inputStream,
|
||||||
|
String fileName,
|
||||||
|
Class<?> targetClass,
|
||||||
|
List<ExcelColumnMapping> columnMappings,
|
||||||
|
Map<String, Object> options
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
## 完整示例
|
||||||
|
|
||||||
|
参考 `ExcelUtilsExample.java` 查看完整使用示例。
|
||||||
|
|
||||||
@@ -53,6 +53,9 @@
|
|||||||
<!-- Redis 依赖版本 -->
|
<!-- Redis 依赖版本 -->
|
||||||
<spring-data-redis.version>3.5.4</spring-data-redis.version>
|
<spring-data-redis.version>3.5.4</spring-data-redis.version>
|
||||||
|
|
||||||
|
<!-- excel -->
|
||||||
|
<poi.version>5.2.3</poi.version>
|
||||||
|
|
||||||
<lombok.version>1.18.40</lombok.version>
|
<lombok.version>1.18.40</lombok.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
@@ -252,6 +255,19 @@
|
|||||||
<version>${spring-data-redis.version}</version>
|
<version>${spring-data-redis.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- excel -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.poi</groupId>
|
||||||
|
<artifactId>poi</artifactId>
|
||||||
|
<version>${poi.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.poi</groupId>
|
||||||
|
<artifactId>poi-ooxml</artifactId>
|
||||||
|
<version>${poi.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</dependencyManagement>
|
</dependencyManagement>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
|||||||
Reference in New Issue
Block a user