一个基于POI 进行Excel导入和导出的工具类
1.引入POI依赖
首先maven引入poi依赖包,我这里使用的是4.1.2版本。
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.2</version>
</dependency>
2.相关API介绍
- Workbook: 表示一个excel文件
- Sheet: 表示excel文件中的一个工作薄,通过Workboot创建
- Row: Excel的行,通过Sheet创建
- Cell: 表示一个单元格,通过Row创建
3. ExcelUtils类
import com.oatrc.common.utils.file.FileUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.List;
/**
* @author xhc
* @Date 2020/11/3 13:31
*/
public class ExcelUtils {
/**
* 创建工作簿
* @param fis 文件输入流
* @param fileName 文件名
* @return 返回指定版本的workbook
*/
public static Workbook getWorkbook(InputStream fis, String fileName) throws IOException {
if (FileUtils.isExcel(fileName)){
Workbook workbook = null;
String extension = FileUtils.getFileExtension(fileName);
if (extension.equalsIgnoreCase("xls")){
workbook = new HSSFWorkbook(fis);
}else if (extension.equalsIgnoreCase("xlsx")){
workbook = new XSSFWorkbook(fis);
}
return workbook;
}
return null;
}
/**
* 读取excel文件中的内容
* @param fis 输入流
* @param fileName 文件名称
* @param clazz 生成的对象类型
* @param <T> 生成的对象类型
* @return 返回excel文件中的对象列表
* @throws IOException 抛出IO异常
*/
public static <T> List<T> readList(InputStream fis, String fileName, Class<T> clazz) throws IOException {
Workbook workbook = getWorkbook(fis, fileName);
if (workbook != null){
return readList(workbook, clazz, 0);
}
return null;
}
/**
* 从Excel文件中创建java对象
* @param workbook
* @param clazz
* @param <T>
* @return
*/
public static <T> List<T> readList(Workbook workbook, Class<T> clazz, int sheetIndex) throws IOException {
if (workbook != null && clazz != null){
ExcelVo<T> excelVo = ExcelVo.getInstance(clazz);
if (excelVo != null){
return excelVo.readExcel(workbook, sheetIndex);
}
}
return null;
}
/**
* 创建excel工作簿
* @param rows
* @param clazz
* @param <T>
* @return
*/
public static <T> Workbook createExcel(List<T> rows, Class<T> clazz){
if (rows != null && clazz != null){
ExcelVo<T> excelVo = ExcelVo.getInstance(clazz);
if (excelVo != null){
return excelVo.createExcel(rows);
}
}
return null;
}
/**
* 将excel文件写到客户端
* @param workbook excel工作簿
* @param fileName 客户端下载文件时的文件名
* @param response 文件输出目的地
* @throws IOException 异常信息
*/
public static void write(Workbook workbook, String fileName, HttpServletResponse response) throws IOException {
if (workbook != null && FileUtils.isExcel(fileName)){
response.setContentType("application/vnd.ms-excel");
response.setHeader("content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "utf-8"));
response.setHeader("Access-Control-Expose-Headers", "content-Disposition");
workbook.write(response.getOutputStream());
}
}
/**
* 将excel文件写到客户端
* @param workbook excel工作簿
* @param fileName 客户端下载文件时的文件名
* @param outputStream 文件输出流
* @throws IOException 异常信息
*/
public static void write(Workbook workbook, String fileName, OutputStream outputStream) throws IOException {
if (workbook != null && FileUtils.isExcel(fileName)){
workbook.write(outputStream);
outputStream.flush();
}
}
}
4. Excel注解类
import java.lang.annotation.*;
/**
* 标注在java对象上表示该对象可以进行Excel的读取和创建。
* 没有标注该注解的对象无法使用ExcelUtils进行Excel创建和读取操作。
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Excel {
/**
* 最大支持的行数,当导入的文件行数大于该数值时会抛出ExcelException异常
*/
public int maxRow() default 10000;
/**
* 最大支持的列数,当导入的文件列数大于该数值时会抛出ExcelException异常
*/
public int maxCol() default 100;
/**
* 导出时在excel中行的高度,单位(px)
*/
public short height() default 14;
}
5. ExcelCoumn注解类
import org.apache.poi.ss.usermodel.CellType;
import java.lang.annotation.*;
/**
* excel导入导出时列字段的注解, 标注在java对象的属性上,
* 规范excel单元格内容与java属性之间的转换关系。
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExcelColumn {
/**
* 表格标题
*/
public String value() default "";
/**
* 表格中字段的编号
*/
public int index() default 0;
/**
* 导出时在excel中每个列的宽 单位为字符
*/
public short width() default 16;
/**
* 导出类型
*/
public CellType cellType() default CellType.STRING;
/**
* 当值为空时,创建excel时的默认值
*/
public String defaultValue() default "";
/**
* 日期格式, 如: yyyy/MM/dd hh:mm:ss
*/
public String dateFormat() default "yyyy/MM/dd hh:mm:ss";
/**
* 文字后缀,如% 90 变成90%
*/
public String suffix() default "";
/**
* 读取内容转表达式 (格式: 0=男,1=女,2=未知),将等号前后的数据进行映射。
* 等号左边的数据时java对象的值,右边为excel单元格内容。
*/
public String converter() default "";
}
6.CellConverter类
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* 进行数据的映射,对ExcelColumn注解中的converter字段进行解释
* 如 (0=男,1=女)
*/
public class CellConverter {
private Map<String, String> map = new HashMap<>();
/**
* 创建Converter对象
* @param pattern 格式必须是 k1=v1,k2=v2的字符串,中间为英文逗号
*/
public CellConverter(String pattern){
String[] values = pattern.split(",");
for (String value : values){
String[] data = value.split("=");
map.put(data[0], data[1]);
}
}
/**
* 导入数据时,将excel的数据转成java数据
* @param val 映射前的数据
* @return 返回映射后的数据
*/
String getObjVal(String val){
Set<String> keySet = map.keySet();
for (String key : keySet){
if (map.get(key).equals(val)){
return key;
}
}
return null;
}
/**
* 导出数据时将java数据转成excel明文信息
* @param key 映射前的数据
* @return 返回映射后的数据
*/
String getExcelVal(String key){
return map.get(key);
}
}
7. 核心对象 ExcelVo
import com.oatrc.common.exception.ExcelException;
import com.oatrc.common.utils.string.StringUtils;
import lombok.Data;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* 实际进行Excel导入和创建操作的对象
* @author xhc
* @Date 2020/11/3 13:31
*/
@Data
public class ExcelVo<T> {
private static final Logger logger = LoggerFactory.getLogger(ExcelVo.class);
private int maxRow; //最大行数
private int maxCol; //最大列数
private short rowHeight; //行高
private List<Column> columns; //表格的列信息
private List<T> objList; //Java对象
private Workbook workbook; //excel工作簿
private Class<T> rowType; //生成Excel行数据的java对象类型
private final Map<String, CellConverter> cellConverterMap = new HashMap<>(); //存放字段的翻译对象
/**
* 禁止使用new关键字来创建实例
*/
private ExcelVo(Class<T> rowType){
this.rowType = rowType;
}
/**
* 获取一个实例对象,检查传入的对象是否有Excel注解,若没有则返回null
* @param clazz excel映射的java类型
* @return 返回一个ExcelVo对象
*/
public static <T> ExcelVo<T> getInstance(Class<T> clazz){
//通过clazz对象填充maxRow,maxCol,columns数据
Excel excel = clazz.getDeclaredAnnotation(Excel.class);
if (excel == null){
throw new ExcelException(clazz.getName() + "不支持Excel操作");
}
ExcelVo<T> excelVo = new ExcelVo<>(clazz);
//获取java类上注解修饰的信息
excelVo.setMaxRow(excel.maxRow());
excelVo.setMaxCol(excel.maxCol());
excelVo.setRowHeight((short) (excel.height() * 20));
//获取每列的元信息
Field[] fields = clazz.getDeclaredFields();
List<Column> columns = createColumns(fields, excelVo.getCellConverterMap());
if (columns.size() > excelVo.maxCol){
throw new ExcelException("Excel数据超出限制,最大支持" + excelVo.maxCol + "列数据");
}
excelVo.setColumns(columns);
return excelVo;
}
/**
* 填充列的元数据信息
* @param fields
* @return
*/
private static List<Column> createColumns(Field[] fields, Map<String, CellConverter> cellConverterMap){
List<Column> columns = new ArrayList<>();
for (Field field : fields){
ExcelColumn annotation = field.getDeclaredAnnotation(ExcelColumn.class);
if (annotation != null){
Column column = new Column();
column.setFieldName(field.getName());
column.setName(annotation.value());
column.setIndex(annotation.index());
column.setWidth(annotation.width());
column.setDefaultValue(annotation.defaultValue());
column.setDateFormat(annotation.dateFormat());
column.setSuffix(annotation.suffix());
column.setFieldType(field.getType().getSimpleName().toLowerCase());
column.setConvert(annotation.converter());
if (StringUtils.isNotEmpty(annotation.converter())){
cellConverterMap.put(column.getFieldName(), new CellConverter(annotation.converter()));
}
columns.add(column);
}
}
return columns;
}
/*----------------------------------------- READ ---------------------------------------*/
/**
* 读取excel文件中的数据
* @param workbook excel文件
* @return 读取结果
*/
public List<T> readExcel(Workbook workbook, int sheetIndex){
this.objList = new ArrayList<>();
if (workbook != null){
this.workbook = workbook;
Sheet sheet = workbook.getSheetAt(sheetIndex);
//获取工作表的行数
int rowCount = sheet.getPhysicalNumberOfRows();
if (rowCount > maxRow + 1){
throw new ExcelException("Excel数据超出限制,最大支持" + maxRow + "行数据");
}
if (rowCount > 1){
//从第二行开始读取数据,第一行是标题,忽略
for (int i = 1; i < rowCount; i++){
Row row = sheet.getRow(i);
T rowValue = setRowValue(row);
if (rowValue != null){
this.objList.add(rowValue);
}
}
}
}
return this.objList;
}
/**
* 获取一个行记录
* @return
*/
private T setRowValue(Row row){
T rowObj = null;
try {
rowObj = this.rowType.newInstance();
for (Column column: this.columns){
//获取cell中的数据
Cell cell = row.getCell(column.getIndex());
//excel中的原始数据
String cellVal = getCellValue(cell);
if (StringUtils.isNotEmpty(cellVal)){
//如果定义了数据转换对象,则对数据进行转换
CellConverter converter = this.getCellConverter(column.getFieldName());
if (converter != null){
cellVal = converter.getObjVal(cellVal);
}
try {
if (StringUtils.isNotEmpty(cellVal)){
Field field = this.rowType.getDeclaredField(column.getFieldName());
field.setAccessible(true); //使属性可以被操作
setCellValue(field, rowObj, cellVal, column.getFieldType());
}
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
System.out.println(rowObj);
return rowObj;
}
/**
* 获取Excel单元格的数据
* @param cell 单元格
* @return 返回单元格中的数据
*/
private String getCellValue(Cell cell){
if (cell != null){
cell.getCellStyle();
CellType cellType = cell.getCellType();
switch (cellType) {
case STRING: return cell.getStringCellValue();
case BOOLEAN: return getBooleanValue(cell).toString();
case NUMERIC: return getNumericValue(cell).toString();
case FORMULA: return getFormulaValue(cell).toString();
default: return null;
}
}
return null;
}
private Boolean getBooleanValue(Cell cell){
return cell.getBooleanCellValue();
}
/**
* 获取单元格中用公式生成的数据
*/
private Double getFormulaValue(Cell cell){
FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator();
evaluator.evaluateFormulaCell(cell);
CellValue cellValue = evaluator.evaluate(cell);
return cellValue.getNumberValue();
}
/**
* 获取单元格中的数字类型的数据
*/
private Object getNumericValue(Cell cell){
Object columnValue = null;
String cellStyleStr = cell.getCellStyle().getDataFormatString();
if ("@".equalsIgnoreCase(cellStyleStr) || "General".equalsIgnoreCase(cellStyleStr)) {
columnValue = cell.getNumericCellValue();
} else {
columnValue = DateUtil.getJavaDate(cell.getNumericCellValue()).getTime();
}
return columnValue;
}
/**
* 填充字段数据
*/
private void setCellValue(Field field, Object obj, String val, String fieldType) throws IllegalAccessException {
if (fieldType.equalsIgnoreCase("string")){
field.set(obj, val);
} else if (fieldType.equalsIgnoreCase("BigDecimal")){
field.set(obj, new BigDecimal(val));
} else if (fieldType.equalsIgnoreCase("BigInteger")){
field.set(obj, new BigInteger(val));
} else if (fieldType.equalsIgnoreCase("int") || fieldType.equalsIgnoreCase("integer")){
field.set(obj, Integer.valueOf(val));
} else if (fieldType.equalsIgnoreCase("long")){
field.set(obj, Long.valueOf(val));
} else if (fieldType.equalsIgnoreCase("short")){
field.set(obj, Short.valueOf(val));
} else if (fieldType.equalsIgnoreCase("byte")){
field.set(obj, Byte.valueOf(val));
} else if (fieldType.equalsIgnoreCase("double")){
field.set(obj, Double.valueOf(val));
} else if (fieldType.equalsIgnoreCase("float")){
field.set(obj, Float.valueOf(val));
} else if (fieldType.equalsIgnoreCase("boolean")){
field.set(obj, Boolean.valueOf(val));
}else if (fieldType.equalsIgnoreCase("date")){
field.set(obj, new Date(Long.parseLong(val)));
}
}
/*----------------------------------------- WRITE ---------------------------------------*/
/**
* 创建excel工作簿
* @param objList java集合
* @return excel工作簿
*/
public Workbook createExcel(List<T> objList){
this.objList = objList;
//创建workbook
Workbook workbook = new SXSSFWorkbook(1000);
//创建工作薄
Sheet sheet = workbook.createSheet();
//创建标题
Row title = sheet.createRow(0);
title.setHeight(this.rowHeight);
fillTitle(title);
//填充内容
for (int i = 0; i < objList.size(); i++) {
Row row = sheet.createRow(i + 1);
row.setHeight(this.rowHeight);
fillRow(row, objList.get(i));
}
this.workbook = workbook;
return workbook;
}
/**
* 填充标题信息
* @param title
*/
private void fillTitle(Row title) {
for (Column column : columns){
//创建单元格并设置列名称以及列的样式
Cell cell = title.createCell(column.getIndex());
cell.setCellValue(column.getName());
}
}
/**
* 填充excel文件内容
* @param row
* @param obj
*/
private void fillRow(Row row, T obj) {
for (Column column : columns){
//创建单元格并设置列名称以及列的样式
Cell cell = row.createCell(column.getIndex());
setCellValue(cell, column, obj);
}
}
private void setCellValue(Cell cell, Column column, T obj) {
try {
Field field = obj.getClass().getDeclaredField(column.getFieldName());
field.setAccessible(true);
Object val = field.get(obj);
if (val != null){
//判断是否有Convert数据映射对象
CellConverter converter = this.getCellConverter(column.getFieldName());
if (converter != null){
val = converter.getExcelVal(val.toString());
}
//判断数据是否为时间类型
if (val instanceof Date){
SimpleDateFormat dateFormat = new SimpleDateFormat(column.getDateFormat());
val = dateFormat.format((Date) val);
}
cell.setCellValue(val.toString());
}else{
cell.setCellValue(column.getDefaultValue());
}
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
/*------------------------------------- COMMON -------------------------------------------*/
/**
* 获取指定字段的字段翻译对象
* @param fieldName 字段名称
* @return 处理该字段java对象和excel字段的映射的CellConverter对象
*/
private CellConverter getCellConverter(String fieldName){
return this.cellConverterMap.get(fieldName);
}
/**
* 隐藏对cellConverterMap的外部访问权限
*/
private Map<String, CellConverter> getCellConverterMap(){
return this.cellConverterMap;
}
}
注:代码中使用到了FileUtils工具类以及StringUtils等工具类,这些工具类只是为了简化代码开发的,代码很简单,不在此处贴出了,根据方法名称便可以知道该方法是干嘛用的,若有兴趣,可以私信笔者或者加QQ:82871987索要代码。
另外ExcelException只是简单的继承了RuntimeException类。