Excel导入导出数据在项目中还是比较常用,这次正好遇到了这个问题,就写了个简单的通用的导入导出工具。
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poi-ooxmlartifactId>
<version>3.17version>
dependency>
.
└── excel
├── ExcelField.java
├── Excel.java
├── ExcelPolicy.java
├── ExcelUtil.java
└── exception
├── ExcelEntityException.java
└── UnknownExcelTypeException.java
具体实现
1 ExcelPolicy.java
package cn.polysys.util.excel;
/**
* 在实体上加{@link Excel}注解时解析策略
*
* @author fantome
* @date 2018/07/05
*/
public enum ExcelPolicy {
/**
* 解析所有字段
*/
ALL,
/**
* 只解析带有{@link Excel}注解的字段
*/
SPECIFY
}
2 Excel.java
注解添加在数据导入导出的实体(Plain Object)上或者字段上。
当@Excel注解添加在实体上并policy设置为ALL则对实体的所有字段解析,字段上有@Excel注解的按注解配置解析,无注解的按默认配置解析。并且要求实体类有完备并与字段严格映射的set和get方法。
package cn.polysys.util.excel;
import java.lang.annotation.*;
/**
* @author fantome
* @date 2018/07/05
*/
@Target({ElementType.FIELD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Excel {
/**
* 默认,等同name,name不为空则取name值
*
* @return
*/
String value() default "";
/**
* 默认,等同value
*
* @return
*/
String name() default "";
/**
* 优先级
*
* @return
*/
int order() default Integer.MAX_VALUE;
/**
* 默认指定解析策略
*
* @return
*/
ExcelPolicy policy() default ExcelPolicy.SPECIFY;
}
3 ExcelField.java
字段操作对象,包含字段的set和get方法,便于解析时数据读取和写入。
package cn.polysys.util.excel;
import java.lang.reflect.Method;
/**
* @author fantome
* @date 2018/07/05
*/
class ExcelField implements Comparable {
/**
* 所在的class
*/
private Class> clazz;
/**
* 列名
*/
private String name;
/**
* 映射属性field名
*/
private String fieldName;
/**
* field的类型
*/
private Class> type;
/**
* 导入导出时顺序,不定义则按默认field顺序
*/
private int order;
/**
* 赋值方法
*/
private Method setter;
/**
* 取值方法
*/
private Method getter;
ExcelField(Class> clazz, String name, String fieldName, Class> type, int order) {
this.clazz = clazz;
this.name = name;
this.fieldName = fieldName;
this.type = type;
this.order = order;
}
Method getSetter() throws NoSuchMethodException {
if (setter == null) {
char[] chars = fieldName.toCharArray();
chars[0] = Character.toUpperCase(chars[0]);
setter = clazz.getMethod("set" + String.valueOf(chars), type);
}
return setter;
}
Method getGetter() throws NoSuchMethodException {
if (getter == null) {
char[] chars = fieldName.toCharArray();
chars[0] = Character.toUpperCase(chars[0]);
getter = clazz.getMethod("get" + String.valueOf(chars));
}
return getter;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getFieldName() {
return fieldName;
}
public void setFieldName(String fieldName) {
this.fieldName = fieldName;
}
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
public Class> getType() {
return type;
}
public void setType(Class> type) {
this.type = type;
}
@Override
public int compareTo(ExcelField o) {
return this.order - o.order;
}
}
4 ExcelEntityException.java
实体存在反射操作异常时抛出,一般为实体定义格式不规范,或者无相关方法。
package cn.polysys.util.excel.exception;
/**
* @author fantome
* @date 2018/07/09
*/
public class ExcelEntityException extends Exception {
public ExcelEntityException() {
super();
}
public ExcelEntityException(String message) {
super(message);
}
public ExcelEntityException(String message, Throwable cause) {
super(message, cause);
}
public ExcelEntityException(Throwable cause) {
super(cause);
}
}
5 UnknownExcelTypeException.java
支持.xls和.xlsx拓展名excel文件解析处理,传入参数为file时根据拓展名判断处理。
package cn.polysys.util.excel.exception;
/**
* @author fantome
* @date 2018/07/09
*/
public class UnknownExcelTypeException extends Exception {
public UnknownExcelTypeException() {
super();
}
public UnknownExcelTypeException(String message) {
super(message);
}
public UnknownExcelTypeException(String message, Throwable cause) {
super(message, cause);
}
public UnknownExcelTypeException(Throwable cause) {
super(cause);
}
}
6 ExcelUtil.java (核心内容)
package cn.polysys.util.excel;
import cn.polysys.util.excel.exception.ExcelEntityException;
import cn.polysys.util.excel.exception.UnknownExcelTypeException;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author fantome
* @date 2018/07/04
*/
public class ExcelUtil {
/**
* 缓存解析数据的操作excelField
*/
private static final Map> EXCEL_FIELD_CACHE = new ConcurrentHashMap<>();
/**
* 导入sheet中数据
*
* @param clazz 数据的POJO类型
* @param sheet 操作的sheet对象
* @param isFirstTitleRow 第一行是标题名称
* @param 数据POJO泛型
* @return 返回导入的数据集
* @throws ExcelEntityException POJO反射操作发生异常时抛出
*/
public static List importExcel(Class clazz, Sheet sheet, boolean isFirstTitleRow) throws ExcelEntityException {
List resultList = new ArrayList<>();
List fields = getExcelFields(clazz);
int rowIndex = 0;
if (isFirstTitleRow) {
rowIndex = 1;
}
for (; rowIndex <= sheet.getLastRowNum(); rowIndex++) {
Row row = sheet.getRow(rowIndex);
try {
T instance = clazz.newInstance();
for (int i = 0; i < fields.size(); i++) {
ExcelField field = fields.get(i);
Class> type = field.getType();
Cell cell = row.getCell(i, Row.MissingCellPolicy.RETURN_BLANK_AS_NULL);
if (cell == null) {
continue;
}
CellType cellType = cell.getCellTypeEnum();
if (cellType.equals(CellType.NUMERIC)) {
if (type.isAssignableFrom(Date.class)) {
field.getSetter().invoke(instance, cell.getDateCellValue());
} else if (type.isAssignableFrom(Integer.class)) {
field.getSetter().invoke(instance, Double.valueOf(cell.getNumericCellValue()).intValue());
} else if (type.isAssignableFrom(BigDecimal.class)) {
BigDecimal decimal = BigDecimal.valueOf(cell.getNumericCellValue());
field.getSetter().invoke(instance, decimal);
} else if (type.isAssignableFrom(Double.class)) {
field.getSetter().invoke(instance, cell.getNumericCellValue());
} else {
field.getSetter().invoke(instance, cell.getStringCellValue());
}
} else {
field.getSetter().invoke(instance, cell.getStringCellValue());
}
}
resultList.add(instance);
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
throw new ExcelEntityException(e);
}
}
return resultList;
}
/**
* @param clazz 数据的POJO类型
* @param workbook 操作的workbook对象
* @param isOnlyFirstSheet 是否只解析读取第一个sheet数据
* @param isFirstTitleRow 第一行是标题名称
* @param 数据POJO泛型
* @return 返回导入的数据集
* @throws ExcelEntityException POJO反射操作发生异常时抛出
*/
public static List importExcel(Class clazz, Workbook workbook, boolean isOnlyFirstSheet, boolean isFirstTitleRow) throws ExcelEntityException {
List resultList = new ArrayList<>();
int numberOfSheets = isOnlyFirstSheet ? 1 : workbook.getNumberOfSheets();
for (int sheetIndex = 0; sheetIndex < numberOfSheets; sheetIndex++) {
Sheet sheet = workbook.getSheetAt(sheetIndex);
List sheetResult = importExcel(clazz, sheet, isFirstTitleRow);
resultList.addAll(sheetResult);
}
return resultList;
}
/**
* @param clazz 数据的POJO类型
* @param workbook 操作的workbook对象
* @param isFirstTitleRow 第一行是标题名称
* @param 数据POJO泛型
* @return 返回导入的数据集
* @throws ExcelEntityException POJO反射操作发生异常时抛出
*/
public static List importExcel(Class clazz, Workbook workbook, boolean isFirstTitleRow) throws ExcelEntityException {
return importExcel(clazz, workbook, false, isFirstTitleRow);
}
/**
* 该方法默认为所有sheet数据格式类型一直,对应实体类型为T
*
* @param clazz 数据的POJO类型
* @param file 需要操作的file
* @param isOnlyFirstSheet 是否只解析读取第一个sheet数据
* @param isFirstTitleRow 第一行是标题名称
* @param 数据POJO泛型
* @return List 导入解析的数据
* @throws IOException 发生文件I/O读写操作异常时抛出
* @throws ExcelEntityException POJO反射操作发生异常时抛出
* @throws UnknownExcelTypeException 未知的excel文件拓展名
*/
public static List importExcel(Class clazz, File file, boolean isOnlyFirstSheet, boolean isFirstTitleRow) throws IOException, ExcelEntityException, UnknownExcelTypeException {
Workbook workbook = file2Workbook(file, false);
return importExcel(clazz, workbook, isOnlyFirstSheet, isFirstTitleRow);
}
/**
* 多组数据导入
*
* @param classes 数据的POJO类型集合
* @param workbook 导入操作的workbook
* @param isFirstTitleRow 第一行是标题名称
* @return 从excel导入的数据
* @throws ExcelEntityException POJO反射操作发生异常时抛出
*/
public static List> importExcelWithMultiType(List> classes, Workbook workbook, boolean isFirstTitleRow) throws ExcelEntityException {
List> resultData = new ArrayList<>();
// 按提供的类型或者实际sheet数量少者解析,避免发生异常
int size = Math.min(classes.size(), workbook.getNumberOfSheets());
for (int index = 0; index < size; index++) {
List> list = importExcel(classes.get(index), workbook.getSheetAt(index), isFirstTitleRow);
resultData.add(list);
}
return resultData;
}
/**
* 多组数据导入
*
* @param classes 数据的POJO类型集合
* @param file 导入的文件
* @param isFirstTitleRow 第一行是标题名称
* @return 从excel导入的数据
* @throws IOException 发生文件I/O读写操作异常时抛出
* @throws ExcelEntityException POJO反射操作发生异常时抛出
* @throws UnknownExcelTypeException 未知的excel文件拓展名
*/
public static List> importExcelWithMultiType(List> classes, File file, boolean isFirstTitleRow) throws IOException, ExcelEntityException, UnknownExcelTypeException {
Workbook workbook = file2Workbook(file, false);
return importExcelWithMultiType(classes, workbook, isFirstTitleRow);
}
/**
* 数据导入Sheet
*
* @param clazz 数据的POJO类型
* @param data 需要导出的数据
* @param sheet 导出数据所在的sheet
* @param isFirstTitleRow 第一行是标题名称
* @throws ExcelEntityException POJO反射操作发生异常时抛出
*/
public static void exportExcel(Class> clazz, List> data, Sheet sheet, boolean isFirstTitleRow) throws ExcelEntityException {
List fields = getExcelFields(clazz);
int rowNum = 0;
if (isFirstTitleRow) {
Row titleRow = sheet.createRow(rowNum++);
for (int i = 0; i < fields.size(); i++) {
Cell cell = titleRow.createCell(i, CellType.STRING);
cell.setCellValue(fields.get(i).getName());
}
}
for (Object obj : data) {
Row row = sheet.createRow(rowNum++);
for (int i = 0; i < fields.size(); i++) {
Cell cell = row.createCell(i);
ExcelField field = fields.get(i);
try {
Object value = field.getGetter().invoke(obj);
Class> type = field.getType();
if (type.isAssignableFrom(Date.class)) {
cell.setCellValue((Date) value);
} else if (type.isAssignableFrom(Boolean.class)) {
cell.setCellValue((Boolean) value);
} else if (type.isAssignableFrom(Integer.class)) {
cell.setCellValue((Integer) value);
} else if (type.isAssignableFrom(BigDecimal.class)) {
BigDecimal decimal = (BigDecimal) value;
cell.setCellValue(decimal.doubleValue());
} else {
cell.setCellValue(String.valueOf(value));
}
} catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
throw new ExcelEntityException(e);
}
}
}
}
/**
* @param clazz 数据的POJO类型
* @param data 需要导出的数据
* @param workbook 导出操作的workbook对象
* @param sheetName 导出时对应sheet名称
* @param isFirstTitleRow 第一行是标题名称
* @param 数据POJO泛型
* @throws ExcelEntityException POJO反射操作发生异常时抛出
*/
public static void exportExcel(Class clazz, List data, Workbook workbook, String sheetName, boolean isFirstTitleRow) throws ExcelEntityException {
Sheet sheet = sheetName == null ? workbook.createSheet() : workbook.createSheet(sheetName);
exportExcel(clazz, data, sheet, isFirstTitleRow);
}
/**
* @param clazz 数据的POJO类型
* @param data 需要导出的数据
* @param sheetName 导出excel的sheet名称,为null使用默认
* @param file 导出到文件
* @param isFirstTitleRow 第一行是标题名称
* @param 数据POJO泛型
* @throws IOException 发生文件I/O读写操作异常时抛出
* @throws ExcelEntityException POJO反射操作发生异常时抛出
* @throws UnknownExcelTypeException 未知的excel文件拓展名
*/
public static void exportExcel(Class clazz, List data, File file, String sheetName, boolean isFirstTitleRow) throws IOException, ExcelEntityException, UnknownExcelTypeException {
Workbook workbook = file2Workbook(file, true);
exportExcel(clazz, data, workbook, sheetName, isFirstTitleRow);
workbook.write(new FileOutputStream(file));
}
/**
* 多组数据导出到多个sheet
*
* @param clazz 数据的POJO类型
* @param data 需要导出的数据
* @param sheetNames 导出excel的每组sheet名称
* @param file 导出到文件
* @param isFirstTitleRow 第一行是标题名称
* @param 数据POJO泛型
* @throws IOException 发生文件I/O读写操作异常时抛出
* @throws ExcelEntityException POJO反射操作发生异常时抛出
* @throws UnknownExcelTypeException 未知的excel文件拓展名
*/
public static void exportExcel(Class clazz, List> data, List sheetNames, File file, boolean isFirstTitleRow) throws IOException, ExcelEntityException, UnknownExcelTypeException {
Workbook workbook = file2Workbook(file, true);
for (int i = 0; i < data.size(); i++) {
String sheetName = i < sheetNames.size() ? sheetNames.get(i) : null;
exportExcel(clazz, data.get(i), workbook, sheetName, isFirstTitleRow);
}
workbook.write(new FileOutputStream(file));
}
/**
* 多类型数据导入到一个WorkBook
*
* @param classes 数据的POJO类型集合
* @param data 数据结合,与classes一一映射
* @param sheetNames 对应sheet名称集合
* @param workbook 导出操作的workbook对象
* @param isFirstTitleRow 第一行是标题名称
* @throws ExcelEntityException POJO反射操作发生异常时抛出
*/
public static void exportExcelWithMultiType(List> classes, List> data, List sheetNames, Workbook workbook, boolean isFirstTitleRow) throws ExcelEntityException {
int size = Math.min(classes.size(), data.size());
for (int i = 0; i < size; i++) {
String sheetName = null;
if (i < sheetNames.size()) {
sheetName = sheetNames.get(i);
}
Sheet sheet = sheetName == null ? workbook.createSheet() : workbook.createSheet(sheetName);
exportExcel(classes.get(i), data.get(i), sheet, isFirstTitleRow);
}
}
/**
* @param classes 数据的POJO类型集合
* @param data 数据结合,与classes一一映射
* @param sheetNames 对应sheet名称集合
* @param file 导出操作的文件对象
* @param isFirstTitleRow 第一行是标题名称
* @throws IOException 发生文件I/O读写操作异常时抛出
* @throws UnknownExcelTypeException 未知的excel文件拓展名
* @throws ExcelEntityException POJO反射操作发生异常时抛出
*/
public static void exportExcelWithMultiType(List> classes, List> data, List sheetNames, File file, boolean isFirstTitleRow) throws IOException, UnknownExcelTypeException, ExcelEntityException {
Workbook workbook = file2Workbook(file, true);
exportExcelWithMultiType(classes, data, sheetNames, workbook, isFirstTitleRow);
workbook.write(new FileOutputStream(file));
}
/**
* @param file 需要操作的file
* @param isExportMode 为export模式时返回空对象,否则从文件流构建对象
* @return Workbook 生成的Workbook对象
* @throws IOException 发生文件I/O读写操作异常时抛出
* @throws UnknownExcelTypeException 未知的excel文件拓展名
*/
public static Workbook file2Workbook(File file, boolean isExportMode) throws UnknownExcelTypeException, IOException {
Workbook workbook;
String fileName = file.getName().toLowerCase();
if (fileName.endsWith(ExcelType.XLS)) {
workbook = isExportMode ? new HSSFWorkbook() : new HSSFWorkbook(new FileInputStream(file));
} else if (fileName.endsWith(ExcelType.XLSX)) {
workbook = isExportMode ? new XSSFWorkbook() : new XSSFWorkbook(new FileInputStream(file));
} else {
throw new UnknownExcelTypeException();
}
return workbook;
}
/**
* @param clazz 数据的POJO类型
* @return 返回class字段操作对象
*/
private static List getExcelFields(Class> clazz) {
if (!EXCEL_FIELD_CACHE.containsKey(clazz)) {
List excelFields = new ArrayList<>();
// 检测是否是全导出策略
boolean allExportPolicy = false;
if (clazz.isAnnotationPresent(Excel.class)) {
ExcelPolicy policy = clazz.getAnnotation(Excel.class).policy();
allExportPolicy = policy.equals(ExcelPolicy.ALL);
}
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (!allExportPolicy && !field.isAnnotationPresent(Excel.class)) {
continue;
}
String name;
int order = Integer.MAX_VALUE;
if (field.isAnnotationPresent(Excel.class)) {
Excel excel = field.getAnnotation(Excel.class);
order = excel.order();
name = excel.name();
if ("".equals(name)) {
name = excel.value();
}
if ("".equals(name)) {
name = field.getName();
}
} else {
name = field.getName();
}
excelFields.add(new ExcelField(clazz, name, field.getName(), field.getType(), order));
}
Collections.sort(excelFields);
EXCEL_FIELD_CACHE.put(clazz, excelFields);
}
return EXCEL_FIELD_CACHE.get(clazz);
}
class ExcelType {
final static String XLS = ".xls";
final static String XLSX = ".xlsx";
}
}
poly-util源代码