在日常开发过程中, 对Excel表格中的数据读取非常常见,比如根据指定模板将数据导入进数据库等等。
以下代码仅仅实现简单的从excel中实现读取,更复杂的表头及数据解析,请自行封装。
1. 实现代码
-
导入依赖
<dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>1.1.1</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.4</version> </dependency>
-
编写MyStringUtils ,去除空格使用
package org.example.excel; public class MyStringUtils { private MyStringUtils() {} /** * 与StringUtils.isEmpty()的区别在于只有空格的字符串也返回true * @param cs * @return */ private static boolean isBlank(final CharSequence cs) { int strLen; if (cs == null || (strLen = cs.length()) == 0) { return true; } for (int i = 0; i < strLen; i++) { if (!Character.isWhitespace(cs.charAt(i))) { return false; } } return true; } public static boolean isNotBlank(final CharSequence cs) { return !isBlank(cs); } }
-
实现对象之间的copy
package org.example.excel; import org.springframework.beans.*; import org.springframework.beans.propertyeditors.CustomDateEditor; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.support.DefaultConversionService; import org.springframework.core.convert.support.GenericConversionService; import org.springframework.util.Assert; import java.beans.PropertyDescriptor; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; public class BeanCopy { private final class DateToStringConverter implements Converter<Date, String> { private DateFormat df ; private DateToStringConverter(String format) { df = new SimpleDateFormat(format); } @Override public String convert(Date source) { return df.format(source); } } private static final String DATE_FORMAT = "yyyy-MM-dd"; private static Object convertForProperty(Wrapper wrapper, Object object, Object value, String propertyName) throws TypeMismatchException { Object result; if (wrapper == null) { result = null; } else { wrapper.setWrappedInstance(object); result = wrapper.getBeanWrapper().convertForProperty(value, propertyName); } return result; } private static Object copyProperties(Object source, Object target) throws BeansException { Wrapper wrapper = new BeanCopy().new Wrapper(source); copyProperties(wrapper, source, target); return target; } private static void copyProperties(Wrapper wrapper, Object source, Object target) throws BeansException { Assert.notNull(source, "Source must not be null"); Assert.notNull(target, "Target must not be null"); Class<?> actualEditable = target.getClass(); PropertyDescriptor[] targetPds = BeanUtils.getPropertyDescriptors(actualEditable); for (PropertyDescriptor targetPd : targetPds) { if (targetPd.getWriteMethod() != null) { PropertyDescriptor sourcePd = BeanUtils.getPropertyDescriptor(source.getClass(), targetPd.getName()); if ( sourcePd.getReadMethod() != null) { try { Method readMethod = sourcePd.getReadMethod(); if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) { readMethod.setAccessible(true); } Object value = readMethod.invoke(source); // 判断是否类型不一致 if (value != null && !(targetPd.getPropertyType().isInstance(value))) { // 数据转型 value = convertForProperty(wrapper, target, value, targetPd.getName()); } Method writeMethod = targetPd.getWriteMethod(); if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) { writeMethod.setAccessible(true); } writeMethod.invoke(target, value); } catch (Exception ex) { throw new FatalBeanException("Could not copy properties from source to target", ex); } } } } } private final class Wrapper { private GenericConversionService conversion; private BeanWrapperImpl bean; private Wrapper(Object object) { conversion = initDefaultConversionService(); bean = initDefaultBeanWrapper(conversion, object); } private void setWrappedInstance(Object object) { bean.setWrappedInstance(object); } private GenericConversionService initDefaultConversionService() { GenericConversionService conversionService = new DefaultConversionService(); conversionService.addConverter(new DateToStringConverter(DATE_FORMAT)); return conversionService; } private BeanWrapperImpl initDefaultBeanWrapper(@SuppressWarnings("hiding") ConversionService conversion, Object object) { BeanWrapperImpl beanWrapper = new BeanWrapperImpl(object); beanWrapper.setConversionService(conversion); SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT); dateFormat.setLenient(false); beanWrapper.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true)); return beanWrapper; } private BeanWrapperImpl getBeanWrapper() { return bean; } } /** * 复制源对象到目的对象 */ private static void convert(Object source, Object target) { copyProperties(source, target); } public static <T> List<T> convert(List<?> sources, Class<T> targetClass) { List<?> sourcesObj = sources; if (sourcesObj == null) { sourcesObj = Collections.emptyList(); } List<T> targets = new ArrayList<>(sourcesObj.size()); BeanCopy.convert(sourcesObj, targets, targetClass); return targets; } private static <T> void convert(List<?> sources, List<T> targets, Class<T> targetClass) { if (targets == null) { return; } targets.clear(); if (sources == null) { return; } for (Object obj : sources) { try { T target = targetClass.newInstance(); targets.add(target); convert(obj, target); } catch (Exception e) { //do something return; } } } }
-
编写 ExcelListener ,校验对象属性中是否携带 ExcelProperty 注解
package org.example.excel; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.context.AnalysisContext; import com.alibaba.excel.event.AnalysisEventListener; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.List; public class ExcelListener extends AnalysisEventListener { private List<Object> dataList = new ArrayList<>(); /** * 通过 AnalysisContext 对象还可以获取当前 sheet,当前行等数据 */ @Override public void invoke(Object object, AnalysisContext context) { if (!checkObjAllFieldsIsNull(object)) { dataList.add(object); } } @Override public void doAfterAllAnalysed(AnalysisContext context) { } private static final String SERIAL_VERSION_UID = "serialVersionUID"; /** * 判断对象中属性值是否全为空 */ private static boolean checkObjAllFieldsIsNull(Object object) { if (null == object) { return true; } try { for (Field f : object.getClass().getDeclaredFields()) { f.setAccessible(true); //只校验带ExcelProperty注解的属性 ExcelProperty property = f.getAnnotation(ExcelProperty.class); if (property == null || SERIAL_VERSION_UID.equals(f.getName())) { continue; } if (f.get(object) != null && MyStringUtils.isNotBlank(f.get(object).toString())) { return false; } } } catch (Exception e) { //do something } return true; } public List<Object> getDataList() { return dataList; } }
-
核心类 ExcelUtil
package org.example.excel; import com.alibaba.excel.ExcelReader; import com.alibaba.excel.metadata.BaseRowModel; import com.alibaba.excel.metadata.Sheet; import org.apache.commons.lang3.StringUtils; import org.example.entity.Student; import org.springframework.web.multipart.MultipartFile; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.util.List; public class ExcelUtil { /** * 私有化构造方法 */ private ExcelUtil() { } private static List<?> readExcel(String path, Class<? extends BaseRowModel> clazz) throws Exception { if (StringUtils.isNotEmpty(path)) { File file = new File(path); if (!file.exists()) { throw new Exception("文件不存在 !"); } InputStream inputStream = new FileInputStream(file); BufferedInputStream bis = new BufferedInputStream(inputStream); return readExcel(bis, clazz); } return null; } private static List<?> readExcel(File file, Class<? extends BaseRowModel> clazz) throws Exception { if (file != null && file.exists()) { InputStream inputStream = new FileInputStream(file); BufferedInputStream bis = new BufferedInputStream(inputStream); return readExcel(bis, clazz); } return null; } private static List<?> readExcel(InputStream inputStream, Class<? extends BaseRowModel> clazz) throws Exception { return readExcel(inputStream, null, clazz); } private static List<?> readExcel(MultipartFile excelFile, Class<? extends BaseRowModel> clazz) throws Exception { if (excelFile != null) { return readExcel(excelFile.getInputStream(), null, clazz); } return null; } public static List<?> readExcel(InputStream inputStream, Object customContent, Class<? extends BaseRowModel> clazz) throws Exception { ExcelListener excelListener = new ExcelListener(); ExcelReader excelReader; if (inputStream instanceof BufferedInputStream) { excelReader = new ExcelReader(inputStream, customContent, excelListener); } else { excelReader = new ExcelReader(new BufferedInputStream(inputStream), customContent, excelListener); } excelReader.read(new Sheet(1, 1, clazz)); return excelListener.getDataList(); } public static List<Student> readStudentExcel(InputStream inputStream) throws Exception{ List<?> list = readExcel(inputStream,Student.class); return BeanCopy.convert(list, Student.class) ; } }
2. 使用
-
创建excel模板
-
编写与excel模板对应的JavaBean
package org.example.entity; import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.metadata.BaseRowModel; import java.io.Serializable; /** * @author zfl_a * @date 2021/4/11 * @project test-spring-boot-starter */ public class Student extends BaseRowModel implements Serializable { @ExcelProperty(index = 0) private String userName; @ExcelProperty(index = 1) private Integer age ; @ExcelProperty(index = 2) private String className ; @ExcelProperty(index = 3) private Double totalScore ; ...省略getter/setter
-
调用ExcelUtil
InputStream inputStream = new FileInputStream(new File("D:\student.xlsx")); List<Student> students = ExcelUtil.readStudentExcel(inputStream); System.out.println(students);
-
读取内容如下:
3. 使用时注意事项
- 默认从excel表格中第一个sheet中的第一行数据开始读取,设置的地方
- JavaBean要与导入的数据一一对应。属性上使用@ExcelProperty(index = 0),index=0表示从第一个开始读取,以此类推。