Excel工具类优化,新增 FieldType 接口,规范化编程,防止误用。

This commit is contained in:
thinkgem
2020-03-05 18:08:00 +08:00
parent a87f4f25ff
commit 60e1129e0e
10 changed files with 204 additions and 185 deletions

View File

@@ -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.00yyyy-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();
}
}

View File

@@ -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();
}
}

View File

@@ -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=width1个汉字占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

View File

@@ -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;
}
}

View File

@@ -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(){
}
}