Excel工具类优化,新增 FieldType 接口,规范化编程,防止误用。
This commit is contained in:
@@ -9,16 +9,15 @@ import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
@@ -41,10 +40,9 @@ import org.apache.poi.xssf.usermodel.XSSFRichTextString;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||
import com.jeesite.common.codec.EncodeUtils;
|
||||
import com.jeesite.common.collect.ListUtils;
|
||||
import com.jeesite.common.collect.SetUtils;
|
||||
import com.jeesite.common.collect.MapUtils;
|
||||
import com.jeesite.common.lang.ObjectUtils;
|
||||
import com.jeesite.common.lang.StringUtils;
|
||||
import com.jeesite.common.reflect.ReflectUtils;
|
||||
@@ -52,11 +50,12 @@ import com.jeesite.common.utils.excel.annotation.ExcelField;
|
||||
import com.jeesite.common.utils.excel.annotation.ExcelField.Align;
|
||||
import com.jeesite.common.utils.excel.annotation.ExcelField.Type;
|
||||
import com.jeesite.common.utils.excel.annotation.ExcelFields;
|
||||
import com.jeesite.common.utils.excel.fieldtype.FieldType;
|
||||
|
||||
/**
|
||||
* 导出Excel文件(导出“XLSX”格式,支持大数据量导出 @see org.apache.poi.ss.SpreadsheetVersion)
|
||||
* @author ThinkGem
|
||||
* @version 2020-2-19
|
||||
* @version 2020-3-5
|
||||
*/
|
||||
public class ExcelExport implements Closeable{
|
||||
|
||||
@@ -88,9 +87,9 @@ public class ExcelExport implements Closeable{
|
||||
private List<Object[]> annotationList;
|
||||
|
||||
/**
|
||||
* 用于清理缓存
|
||||
* 存储字段类型临时数据
|
||||
*/
|
||||
private Set<Class<?>> fieldTypes = SetUtils.newHashSet();
|
||||
private Map<Class<? extends FieldType>, FieldType> fieldTypes = MapUtils.newHashMap();
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
@@ -249,7 +248,11 @@ public class ExcelExport implements Closeable{
|
||||
}
|
||||
}
|
||||
headerList.add(headerTitle);
|
||||
headerWidthList.add(ef.width());
|
||||
if (ef.words() != -1) {
|
||||
headerWidthList.add(ef.words() * 256);
|
||||
}else {
|
||||
headerWidthList.add(ef.width());
|
||||
}
|
||||
}
|
||||
// 创建工作表
|
||||
this.createSheet(sheetName, title, headerList, headerWidthList);
|
||||
@@ -426,7 +429,7 @@ public class ExcelExport implements Closeable{
|
||||
* @return 单元格对象
|
||||
*/
|
||||
public Cell addCell(Row row, int column, Object val){
|
||||
return this.addCell(row, column, val, Align.AUTO, Class.class, null);
|
||||
return this.addCell(row, column, val, Align.AUTO, FieldType.class, null);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -438,20 +441,17 @@ public class ExcelExport implements Closeable{
|
||||
* @param dataFormat 数值格式(例如:0.00,yyyy-MM-dd)
|
||||
* @return 单元格对象
|
||||
*/
|
||||
public Cell addCell(Row row, int column, Object val, Align align, Class<?> fieldType, String dataFormat){
|
||||
@SuppressWarnings("unchecked")
|
||||
public Cell addCell(Row row, int column, Object val, Align align, Class<? extends FieldType> fieldType, String dataFormat){
|
||||
Cell cell = row.createCell(column);
|
||||
String defaultDataFormat = "@";
|
||||
String defaultDataFormat = null;
|
||||
try {
|
||||
if(val == null){
|
||||
cell.setCellValue("");
|
||||
}else if(fieldType != Class.class){
|
||||
fieldTypes.add(fieldType); // 先存起来,方便完成后清理缓存
|
||||
cell.setCellValue((String)fieldType.getMethod("setValue", Object.class).invoke(null, val));
|
||||
try{
|
||||
defaultDataFormat = (String)fieldType.getMethod("getDataFormat").invoke(null);
|
||||
} catch (Exception ex) {
|
||||
defaultDataFormat = "@";
|
||||
}
|
||||
cell.setCellValue(StringUtils.EMPTY);
|
||||
}else if(fieldType != FieldType.class){
|
||||
FieldType ft = getFieldType(fieldType);
|
||||
cell.setCellValue(ft.setValue(val));
|
||||
defaultDataFormat = ft.getDataFormat();
|
||||
}else{
|
||||
if(val instanceof String) {
|
||||
cell.setCellValue((String) val);
|
||||
@@ -474,10 +474,11 @@ public class ExcelExport implements Closeable{
|
||||
defaultDataFormat = "yyyy-MM-dd HH:mm";
|
||||
}else {
|
||||
// 如果没有指定 fieldType,切自行根据类型查找相应的转换类(com.jeesite.common.utils.excel.fieldtype.值的类名+Type)
|
||||
Class<?> fieldType2 = Class.forName(this.getClass().getName().replaceAll(this.getClass().getSimpleName(),
|
||||
fieldType = (Class<? extends FieldType>)Class.forName(this.getClass().getName().replaceAll(this.getClass().getSimpleName(),
|
||||
"fieldtype."+val.getClass().getSimpleName()+"Type"));
|
||||
fieldTypes.add(fieldType2); // 先存起来,方便完成后清理缓存
|
||||
cell.setCellValue((String)fieldType2.getMethod("setValue", Object.class).invoke(null, val));
|
||||
FieldType ft = getFieldType(fieldType);
|
||||
cell.setCellValue(ft.setValue(val));
|
||||
defaultDataFormat = ft.getDataFormat();
|
||||
}
|
||||
}
|
||||
// if (val != null){
|
||||
@@ -485,9 +486,12 @@ public class ExcelExport implements Closeable{
|
||||
if (style == null){
|
||||
style = wb.createCellStyle();
|
||||
style.cloneStyleFrom(styles.get("data"+(align.value()>=1&&align.value()<=3?align.value():"")));
|
||||
if (dataFormat != null){
|
||||
if (StringUtils.isNotBlank(dataFormat)){
|
||||
defaultDataFormat = dataFormat;
|
||||
}
|
||||
if (defaultDataFormat == null) {
|
||||
defaultDataFormat = "@";
|
||||
}
|
||||
style.setDataFormat(wb.createDataFormat().getFormat(defaultDataFormat));
|
||||
styles.put("data_column_" + column, style);
|
||||
}
|
||||
@@ -499,6 +503,15 @@ public class ExcelExport implements Closeable{
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
private FieldType getFieldType(Class<? extends FieldType> fieldType) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
|
||||
FieldType ft = fieldTypes.get(fieldType);
|
||||
if (ft == null) {
|
||||
ft = fieldType.getDeclaredConstructor().newInstance();
|
||||
fieldTypes.put(fieldType, ft);
|
||||
}
|
||||
return ft;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加数据(通过annotation.ExportField添加数据)
|
||||
@@ -535,17 +548,20 @@ public class ExcelExport implements Closeable{
|
||||
log.info(ex.toString());
|
||||
val = "";
|
||||
}
|
||||
String dataFormat = ef.dataFormat();
|
||||
try {
|
||||
// 获取Json格式化注解的格式化参数
|
||||
JsonFormat jf = e.getClass().getMethod("get"+StringUtils.capitalize(ef.attrName())).getAnnotation(JsonFormat.class);
|
||||
if (jf != null && jf.pattern() != null){
|
||||
dataFormat = jf.pattern();
|
||||
}
|
||||
} catch (Exception e1) {
|
||||
// 如果获取失败,则使用默认。
|
||||
}
|
||||
this.addCell(row, colunm++, val, ef.align(), ef.fieldType(), dataFormat);
|
||||
// // 如果没有设置格式,则获取Json格式化注解的格式化参数(建议使用 ef.dataFormat 指定格式)
|
||||
// String dataFormat = ef.dataFormat();
|
||||
// if (StringUtils.isBlank(dataFormat)) {
|
||||
// try {
|
||||
// JsonFormat jf = e.getClass().getMethod("get"+StringUtils.capitalize(ef.attrName()))
|
||||
// .getAnnotation(JsonFormat.class);
|
||||
// if (jf != null && jf.pattern() != null){
|
||||
// dataFormat = jf.pattern();
|
||||
// }
|
||||
// } catch (Exception e1) {
|
||||
// // 如果获取失败,则使用默认。
|
||||
// }
|
||||
// }
|
||||
this.addCell(row, colunm++, val, ef.align(), ef.fieldType(), ef.dataFormat());
|
||||
sb.append(val + ", ");
|
||||
}
|
||||
log.debug("Write success: ["+row.getRowNum()+"] "+sb.toString());
|
||||
@@ -607,17 +623,14 @@ public class ExcelExport implements Closeable{
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
fieldTypes.clear();
|
||||
if (wb instanceof SXSSFWorkbook){
|
||||
((SXSSFWorkbook)wb).dispose();
|
||||
}
|
||||
Iterator<Class<?>> it = fieldTypes.iterator();
|
||||
while(it.hasNext()){
|
||||
Class<?> clazz = it.next();
|
||||
try {
|
||||
clazz.getMethod("clearCache").invoke(null);
|
||||
} catch (Exception e) {
|
||||
// 报错忽略,有可能没实现此方法
|
||||
}
|
||||
try {
|
||||
wb.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.DecimalFormat;
|
||||
import java.util.Collections;
|
||||
@@ -16,9 +17,8 @@ import java.util.Comparator;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.Map;
|
||||
|
||||
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
|
||||
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
|
||||
import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
|
||||
import org.apache.poi.ss.formula.eval.ErrorEval;
|
||||
@@ -37,7 +37,7 @@ import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import com.jeesite.common.callback.MethodCallback;
|
||||
import com.jeesite.common.collect.ListUtils;
|
||||
import com.jeesite.common.collect.SetUtils;
|
||||
import com.jeesite.common.collect.MapUtils;
|
||||
import com.jeesite.common.lang.DateUtils;
|
||||
import com.jeesite.common.lang.ObjectUtils;
|
||||
import com.jeesite.common.lang.StringUtils;
|
||||
@@ -45,11 +45,12 @@ import com.jeesite.common.reflect.ReflectUtils;
|
||||
import com.jeesite.common.utils.excel.annotation.ExcelField;
|
||||
import com.jeesite.common.utils.excel.annotation.ExcelField.Type;
|
||||
import com.jeesite.common.utils.excel.annotation.ExcelFields;
|
||||
import com.jeesite.common.utils.excel.fieldtype.FieldType;
|
||||
|
||||
/**
|
||||
* 导入Excel文件(支持“XLS”和“XLSX”格式)
|
||||
* @author ThinkGem
|
||||
* @version 2020-2-19
|
||||
* @version 2020-3-5
|
||||
*/
|
||||
public class ExcelImport implements Closeable {
|
||||
|
||||
@@ -71,9 +72,9 @@ public class ExcelImport implements Closeable {
|
||||
private int headerNum;
|
||||
|
||||
/**
|
||||
* 用于清理缓存
|
||||
* 存储字段类型临时数据
|
||||
*/
|
||||
private Set<Class<?>> fieldTypes = SetUtils.newHashSet();
|
||||
private Map<Class<? extends FieldType>, FieldType> fieldTypes = MapUtils.newHashMap();
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
@@ -263,9 +264,9 @@ public class ExcelImport implements Closeable {
|
||||
try{
|
||||
Cell cell = row.getCell(column);
|
||||
if (cell != null){
|
||||
if (cell.getCellTypeEnum() == CellType.NUMERIC){
|
||||
if (cell.getCellType() == CellType.NUMERIC){
|
||||
val = cell.getNumericCellValue();
|
||||
if (HSSFDateUtil.isCellDateFormatted(cell)) {
|
||||
if (DateUtil.isCellDateFormatted(cell)) {
|
||||
val = DateUtil.getJavaDate((Double) val); // POI Excel 日期格式转换
|
||||
}else{
|
||||
if ((Double) val % 1 > 0){
|
||||
@@ -274,17 +275,17 @@ public class ExcelImport implements Closeable {
|
||||
val = new DecimalFormat("0").format(val);
|
||||
}
|
||||
}
|
||||
}else if (cell.getCellTypeEnum() == CellType.STRING) {
|
||||
}else if (cell.getCellType() == CellType.STRING) {
|
||||
val = cell.getStringCellValue();
|
||||
}else if (cell.getCellTypeEnum() == CellType.FORMULA){
|
||||
}else if (cell.getCellType() == CellType.FORMULA){
|
||||
try {
|
||||
val = cell.getStringCellValue();
|
||||
} catch (Exception e) {
|
||||
FormulaEvaluator evaluator = cell.getSheet().getWorkbook()
|
||||
.getCreationHelper().createFormulaEvaluator();
|
||||
evaluator.evaluateFormulaCellEnum(cell);
|
||||
evaluator.evaluateFormulaCell(cell);
|
||||
CellValue cellValue = evaluator.evaluate(cell);
|
||||
switch (cellValue.getCellTypeEnum()) {
|
||||
switch (cellValue.getCellType()) {
|
||||
case NUMERIC:
|
||||
val = cellValue.getNumberValue();
|
||||
break;
|
||||
@@ -301,9 +302,9 @@ public class ExcelImport implements Closeable {
|
||||
val = cell.getCellFormula();
|
||||
}
|
||||
}
|
||||
}else if (cell.getCellTypeEnum() == CellType.BOOLEAN){
|
||||
}else if (cell.getCellType() == CellType.BOOLEAN){
|
||||
val = cell.getBooleanCellValue();
|
||||
}else if (cell.getCellTypeEnum() == CellType.ERROR){
|
||||
}else if (cell.getCellType() == CellType.ERROR){
|
||||
val = cell.getErrorCellValue();
|
||||
}
|
||||
}
|
||||
@@ -348,6 +349,7 @@ public class ExcelImport implements Closeable {
|
||||
* @param isThrowException 遇见错误是否抛出异常
|
||||
* @param groups 导入分组
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public <E> List<E> getDataList(Class<E> cls, MethodCallback exceptionCallback, String... groups) throws InstantiationException, IllegalAccessException{
|
||||
List<Object[]> annotationList = ListUtils.newArrayList();
|
||||
// Get annotation field
|
||||
@@ -426,9 +428,9 @@ public class ExcelImport implements Closeable {
|
||||
//log.debug("Import value type: ["+i+","+column+"] " + valType);
|
||||
try {
|
||||
if (StringUtils.isNotBlank(ef.attrName())){
|
||||
if (ef.fieldType() != Class.class){
|
||||
fieldTypes.add(ef.fieldType()); // 先存起来,方便完成后清理缓存
|
||||
val = ef.fieldType().getMethod("getValue", String.class).invoke(null, val);
|
||||
if (ef.fieldType() != FieldType.class){
|
||||
FieldType ft = getFieldType(ef.fieldType());
|
||||
val = ft.getValue(ObjectUtils.toString(val));
|
||||
}
|
||||
}else{
|
||||
if (val != null){
|
||||
@@ -454,15 +456,15 @@ public class ExcelImport implements Closeable {
|
||||
val = DateUtil.getJavaDate((Double)val); // POI Excel 日期格式转换
|
||||
}
|
||||
}else{
|
||||
if (ef.fieldType() != Class.class){
|
||||
fieldTypes.add(ef.fieldType()); // 先存起来,方便完成后清理缓存
|
||||
val = ef.fieldType().getMethod("getValue", String.class).invoke(null, val.toString());
|
||||
if (ef.fieldType() != FieldType.class){
|
||||
FieldType ft = getFieldType(ef.fieldType());
|
||||
val = ft.getValue(ObjectUtils.toString(val));
|
||||
}else{
|
||||
// 如果没有指定 fieldType,切自行根据类型查找相应的转换类(com.jeesite.common.utils.excel.fieldtype.值的类名+Type)
|
||||
Class<?> fieldType2 = Class.forName(this.getClass().getName().replaceAll(this.getClass().getSimpleName(),
|
||||
Class<? extends FieldType> fieldType = (Class<? extends FieldType>)Class.forName(this.getClass().getName().replaceAll(this.getClass().getSimpleName(),
|
||||
"fieldtype."+valType.getSimpleName()+"Type"));
|
||||
fieldTypes.add(fieldType2); // 先存起来,方便完成后清理缓存
|
||||
val = fieldType2.getMethod("getValue", String.class).invoke(null, val.toString());
|
||||
FieldType ft = getFieldType(fieldType);
|
||||
val = ft.getValue(ObjectUtils.toString(val));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -496,16 +498,22 @@ public class ExcelImport implements Closeable {
|
||||
return dataList;
|
||||
}
|
||||
|
||||
private FieldType getFieldType(Class<? extends FieldType> fieldType) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
|
||||
FieldType ft = fieldTypes.get(fieldType);
|
||||
if (ft == null) {
|
||||
ft = fieldType.getDeclaredConstructor().newInstance();
|
||||
fieldTypes.put(fieldType, ft);
|
||||
}
|
||||
return ft;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
Iterator<Class<?>> it = fieldTypes.iterator();
|
||||
while(it.hasNext()){
|
||||
Class<?> clazz = it.next();
|
||||
try {
|
||||
clazz.getMethod("clearCache").invoke(null);
|
||||
} catch (Exception e) {
|
||||
// 报错忽略,有可能没实现此方法
|
||||
}
|
||||
public void close() throws IOException {
|
||||
fieldTypes.clear();
|
||||
try {
|
||||
wb.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,10 +8,12 @@ import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import com.jeesite.common.utils.excel.fieldtype.FieldType;
|
||||
|
||||
/**
|
||||
* Excel注解定义
|
||||
* @author ThinkGem
|
||||
* @version 2013-03-10
|
||||
* @version 2020-3-5
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.TYPE})
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@@ -59,6 +61,11 @@ public @interface ExcelField {
|
||||
*/
|
||||
int width() default -1;
|
||||
|
||||
/**
|
||||
* 指定导出列宽可以显示的字符个数(words*256=width,1个汉字占2个字符) v4.2.0
|
||||
*/
|
||||
int words() default -1;
|
||||
|
||||
/**
|
||||
* 导出字段字段排序(升序)
|
||||
*/
|
||||
@@ -79,12 +86,12 @@ public @interface ExcelField {
|
||||
* MoneyType.class 金额类型转换(保留两位)
|
||||
* DateTimeType.class 日期时间类型转换 yyyy-MM-dd HH:mm:ss
|
||||
*/
|
||||
Class<?> fieldType() default Class.class;
|
||||
Class<? extends FieldType> fieldType() default FieldType.class;
|
||||
|
||||
/**
|
||||
* 数值格式(例如:数值:0.00;日期:yyyy-MM-dd;金额:¥#,##0.00)
|
||||
*/
|
||||
String dataFormat() default "@";
|
||||
String dataFormat() default "";
|
||||
|
||||
/**
|
||||
* 字段归属组(针对每一种业务的导入、导出) imp、exp
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* Copyright (c) 2013-Now http://jeesite.com All rights reserved.
|
||||
*/
|
||||
package com.jeesite.common.utils.excel.fieldtype;
|
||||
|
||||
/**
|
||||
* Excel字段类型转换
|
||||
* @author ThinkGem
|
||||
* @version 2020-3-5
|
||||
*/
|
||||
public interface FieldType {
|
||||
|
||||
/**
|
||||
* 获取对象值(导入)
|
||||
*/
|
||||
default public Object getValue(String val) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象值(导出)
|
||||
*/
|
||||
default public String setValue(Object val) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象值格式(导出)
|
||||
*/
|
||||
default public String getDataFormat() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -12,38 +12,32 @@ import org.apache.commons.lang3.StringUtils;
|
||||
/**
|
||||
* 金额类型转换(保留两位)
|
||||
* @author ThinkGem
|
||||
* @version 2015-9-29
|
||||
* @version 2020-3-5
|
||||
* @example fieldType = MoneyType.class
|
||||
*/
|
||||
public class MoneyType {
|
||||
public class MoneyType implements FieldType {
|
||||
|
||||
private NumberFormat nf = new DecimalFormat(",##0.00");
|
||||
|
||||
/**
|
||||
* 获取对象值(导入)
|
||||
*/
|
||||
public static Object getValue(String val) {
|
||||
return val == null ? "" : StringUtils.replace(val, ",", "");
|
||||
public Object getValue(String val) {
|
||||
return val == null ? StringUtils.EMPTY : StringUtils.replace(val, ",", StringUtils.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象值(导出)
|
||||
*/
|
||||
public static String setValue(Object val) {
|
||||
NumberFormat nf = new DecimalFormat(",##0.00");
|
||||
return val == null ? "" : nf.format(val);
|
||||
public String setValue(Object val) {
|
||||
return val == null ? StringUtils.EMPTY : nf.format(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取对象值格式(导出)
|
||||
*/
|
||||
public static String getDataFormat() {
|
||||
public String getDataFormat() {
|
||||
return "0.00";
|
||||
}
|
||||
|
||||
/**
|
||||
* 清理缓存
|
||||
*/
|
||||
public static void clearCache(){
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user