基于Poi的注解导入Excel的方式

最近在做一个excel导入功能,项目中用的excel插件是easypoi。 其实easypoi已经可以实现基于注解的导入方式了, 但是在实际调用的时候总是报一个ClassNotFound异常。上网一查有人说是因为jar包版本不匹配导致的。 项目也是一个挺老的项目了, 也不敢贸然升级插件的版本,怕出一些问题。 没有办法,只能自己实现了。

其实这个工具实现的思路还是挺简单的: 首先先新建一个注解类, 然后在业务代码调用导入工具方法的时候去反射读取传入的class对象, 判断这个class对象的字段是否是新加的那个注解,如果是自定义注解就去建立字段与excel列索引的关联关系, 然后凭借着这个关联关系去循环地反射调用通过反射创建的object对象,最后把object加入到List并返回。 这样就可以实现excel转List了。

另外, 这只是一个临时创建的工具类, 只能实现最基本的数据读取。对于数据的校验,格式的验证,内存控制等一些高级功能远远不如easypoi或者easyexcel,如果有条件的话还是不要用我这个

--------------------------------------------------------------------------------------------------------------------------

下面是具体实现

自定义的@Import注解

package com.example.demo.utils;

import java.lang.annotation.*;

/**
 * 可以在导入实体的字段中加入此注解,
 * 调用ImportExcelUtil.excelToList(XSSFWorkbook workbook, Class<?> pojoClass) 将excel文件转化成List<T>集合
 * @author lrxsz
 */
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Import {
    /**
     * 导入列首行的名称, 也就是表头
     * @return
     */
    String name();

    /**
     * 是否是必填项, 如果此属性设置了true,但是excel中的单元格中的某行没有这列数据,则此行不会被读取到list中
     * @return
     */
    boolean must() default false;
}

字段与excel列索引的映射关系类

package com.example.demo.utils;

import lombok.Data;

/**
 * 导入excel工具的exlcel与实体类的映射类
 * @author lrxsz
 */
@Data
public class ImportMap {
    /**
     * 注解的名称
     */
    String annotationName;
    /**
     * 字段的名称
     */
    String fieldName;
    /**
     * 字段的类型
     */
    Class<?> fieldTypeClass;
    /**
     * 所在行的列数(index)
     */
    Integer cellNum;
    /**
     * 是否是必填项, 如果此属性设置了true,但是excel中没有此列数据,则此行不会被读取到list
     */
    Boolean isMust;
}

导入主工具类

package com.example.demo.utils;

import org.apache.poi.hssf.usermodel.*;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.NumberToTextConverter;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;

/**
 * 基于Poi的Excel导入工具类
 * @author lrxsz
 */
public class ImportExcelUtil {
    /**
     * 时间转化类型
     */
    private static final String[] DATEPATTERN = {
      "yyyy-MM-dd","yyyy/MM/dd","yyyyMMdd","yyyy年MM月dd日",
            "yyyy-MM-dd HH:mm:ss","yyyy/MM/dd HH:mm:ss","yyyyMMdd HH:mm:ss","yyyy年MM月dd日 HH:mm:ss"
    };
    private static final SimpleDateFormat format = new SimpleDateFormat();
    /**
     * 获取表头与excel导入实体类字段的对应关系
     * @return
     */
    private static HashMap<Integer, ImportMap> getExcelMap(XSSFWorkbook workbook, Class<?> pojoClass) throws IOException {
        HashMap<Integer, ImportMap> excelMap = new HashMap<Integer, ImportMap>();
        XSSFSheet sheet = workbook.getSheetAt(0); //导入第一个

        XSSFRow header = sheet.getRow(0); //获取表头
        Field[] declaredFields = pojoClass.getDeclaredFields();
        for(int i = 0; declaredFields != null && i < declaredFields.length; i ++) {
            if(declaredFields[i] == null)
                continue;
            Import annotation = declaredFields[i].getAnnotation(Import.class);
            if(annotation == null)
                continue;
            String fieldName = declaredFields[i].getName();
            Class<?> type = declaredFields[i].getType();
            String annotationName = annotation.name();
            boolean must = annotation.must();
            for(int j = 0; header != null && j < header.getLastCellNum(); j ++) {
                XSSFCell cell = header.getCell(j);
                String headerCell = getCellValue(cell);
                if(annotationName != null && annotationName.equals(headerCell)) {
                    ImportMap map = new ImportMap();
                    map.setCellNum(j);
                    map.setAnnotationName(annotationName);
                    map.setFieldName(fieldName);
                    map.setFieldTypeClass(type);
                    map.setIsMust(must);
                    excelMap.put(i, map);
                }
            }
        }
        return excelMap;
    }
    private static HashMap<Integer, ImportMap> getExcelMap(HSSFWorkbook workbook, Class<?> pojoClass) throws IOException {
        HashMap<Integer, ImportMap> excelMap = new HashMap<Integer, ImportMap>();
        //XSSFSheet sheet = workbook.getSheetAt(0); //导入第一个
        HSSFSheet sheet = workbook.getSheetAt(0);

        //XSSFRow header = sheet.getRow(0); //获取表头
        HSSFRow header = sheet.getRow(0);
        Field[] declaredFields = pojoClass.getDeclaredFields();
        for(int i = 0; declaredFields != null && i < declaredFields.length; i ++) {
            if(declaredFields[i] == null)
                continue;
            Import annotation = declaredFields[i].getAnnotation(Import.class);
            if(annotation == null)
                continue;
            String fieldName = declaredFields[i].getName();
            Class<?> type = declaredFields[i].getType();
            String annotationName = annotation.name();
            boolean must = annotation.must();
            for(int j = 0; header != null && j < header.getLastCellNum(); j ++) {
                //XSSFCell cell = header.getCell(j);
                HSSFCell cell = header.getCell(j);
                String headerCell = getCellValue(cell);
                if(annotationName != null && annotationName.equals(headerCell)) {
                    ImportMap map = new ImportMap();
                    map.setCellNum(j);
                    map.setAnnotationName(annotationName);
                    map.setFieldName(fieldName);
                    map.setFieldTypeClass(type);
                    map.setIsMust(must);
                    excelMap.put(i, map);
                }
            }
        }
        return excelMap;
    }
    /**
     * 获取最后一个必填项所在列的列数(index)
     * @param workbook
     * @param excelMap
     * @return
     */
    static int getLastMustIndex(XSSFWorkbook workbook, HashMap<Integer, ImportMap> excelMap) {
        XSSFSheet sheet = workbook.getSheetAt(0); //导入第一个
        XSSFRow header = sheet.getRow(0); //获取表头
        int result = 0;
        for(int i = 0; header != null && i < header.getLastCellNum(); i ++) {
            ImportMap importMap = excelMap.get(i);
            if(importMap != null && importMap.getIsMust()) {
                result = i;
            }
        }
        return result;
    }
    static int getLastMustIndex(HSSFWorkbook workbook, HashMap<Integer, ImportMap> excelMap) {
        //XSSFSheet sheet = workbook.getSheetAt(0); //导入第一个
        HSSFSheet sheet = workbook.getSheetAt(0);
        //XSSFRow header = sheet.getRow(0); //获取表头
        HSSFRow header = sheet.getRow(0);
        int result = 0;
        for(int i = 0; header != null && i < header.getLastCellNum(); i ++) {
            ImportMap importMap = excelMap.get(i);
            if(importMap != null && importMap.getIsMust()) {
                result = i;
            }
        }
        return result;
    }

    public static <T> List<T> excelToList(HSSFWorkbook workbook, Class<?> pojoClass) throws Exception {
        HashMap<Integer, ImportMap> excelMap = getExcelMap(workbook, pojoClass);
        int lastMustIndex = getLastMustIndex(workbook, excelMap);//最后一个必填项的index
        //XSSFSheet sheet = workbook.getSheetAt(0); //默认导入第一个
        HSSFSheet sheet = workbook.getSheetAt(0);
        List list = new ArrayList();
        for (int rowNum = 1; rowNum < sheet.getLastRowNum() + 1 && null != sheet; rowNum++) {
            //XSSFRow row = sheet.getRow(rowNum);
            HSSFRow row = sheet.getRow(rowNum);
            short lastCellNum = row.getLastCellNum();
            if(row != null && lastCellNum < (lastMustIndex + 1)) {
                continue;
            }
            Object object = pojoClass.newInstance();
            for(int i = 0; row != null && i < lastCellNum; i ++) {
                //XSSFCell cell = row.getCell(i);
                HSSFCell cell = row.getCell(i);
                String cellContent = getCellValue(cell);
                ImportMap importMap = excelMap.get(i);
                if(importMap == null) {
                    break;
                }
                if(importMap.getIsMust() && isEmpty(cellContent)) {
                    object = null;
                    break;
                }
                String methodName = "set" + capitalizeFirstLetter(importMap.getFieldName());
                try {
                    Method method = pojoClass.getMethod(methodName, importMap.getFieldTypeClass());
                    if(cellContent == null || cellContent.length() == 0)
                        continue;
                    method.invoke(object, convertType(importMap.getFieldTypeClass(), cellContent));
                } catch (Exception e) {
                    //e.printStackTrace();
                    throw new Exception("\"" + importMap.getAnnotationName() + "\"数据格式不正确");
                }
            }
            if(object != null) {
                list.add(object);
            }
        }
        return list;
    }
    /**
     * Excel转List<T>
     * 此方法仅支持xlsx
     * 需要在导入实体类的字段中加入@Import注解
     * @param workbook
     * @param pojoClass
     * @param <T>
     * @return
     * @throws IllegalAccessException
     * @throws InstantiationException
     * @throws IOException
     * @throws NoSuchFieldException
     * @throws NoSuchMethodException
     * @throws InvocationTargetException
     * @throws ParseException
     */
    public static <T> List<T> excelToList(XSSFWorkbook workbook, Class<?> pojoClass) throws Exception {
        HashMap<Integer, ImportMap> excelMap = getExcelMap(workbook, pojoClass);
        int lastMustIndex = getLastMustIndex(workbook, excelMap);//最后一个必填项的index
        XSSFSheet sheet = workbook.getSheetAt(0); //默认导入第一个
        List list = new ArrayList();
        for (int rowNum = 1; rowNum < sheet.getLastRowNum() + 1 && null != sheet; rowNum++) {
            XSSFRow row = sheet.getRow(rowNum);
            short lastCellNum = row.getLastCellNum();
            if(row != null && lastCellNum < (lastMustIndex + 1)) {
                continue;
            }
            Object object = pojoClass.newInstance();
            for(int i = 0; row != null && i < lastCellNum; i ++) {
                XSSFCell cell = row.getCell(i);
                String cellContent = getCellValue(cell);
                ImportMap importMap = excelMap.get(i);
                if(importMap == null) {
                    break;
                }
                if(importMap.getIsMust() && isEmpty(cellContent)) {
                    object = null;
                    break;
                }
                String methodName = "set" + capitalizeFirstLetter(importMap.getFieldName());
                try {
                    Method method = pojoClass.getMethod(methodName, importMap.getFieldTypeClass());
                    if(cellContent == null || cellContent.length() == 0)
                        continue;
                    method.invoke(object, convertType(importMap.getFieldTypeClass(), cellContent));
                } catch (Exception e) {
                    //e.printStackTrace();
                    throw new Exception("\"" + importMap.getAnnotationName() + "\"数据格式不正确");
                }
            }
            if(object != null) {
                list.add(object);
            }
        }
        return list;
    }
    private static boolean isEmpty(String cellContent) {
        if(cellContent == null) {
            return true;
        }
        if(cellContent.length() == 0) {
            return true;
        }
        return false;
    }
    private static Object convertType(Class<?> fieldTypeClass, String cellContent) throws Exception {
        String name = fieldTypeClass.getName();
        if(fieldTypeClass == String.class) {
            return cellContent.trim();
        }
        if(fieldTypeClass == Integer.class || "int".equals(name)) {
            if(isEmpty(cellContent)) {
                return 0;
            } else {
                return Integer.parseInt(cellContent);
            }
        }
        if(fieldTypeClass == Short.class || "short".equals(name)) {
            if(isEmpty(cellContent)) {
                new Short("0");
            } else {
                return Short.parseShort(cellContent);
            }
        }
        if(fieldTypeClass == Long.class || "long".equals(name)) {
            if(isEmpty(cellContent)) {
                return 0L;
            } else {
                return Long.parseLong(cellContent);
            }
        }
        if(fieldTypeClass == Double.class || "double".equals(name)) {
            if(isEmpty(cellContent)) {
                return 0D;
            } else {
                return Double.parseDouble(cellContent);
            }
        }
        if(fieldTypeClass == Float.class || "float".equals(name)) {
            if(isEmpty(cellContent)) {
                return 0F;
            } else {
                return Float.parseFloat(cellContent);
            }
        }
        if(fieldTypeClass == Byte.class || "byte".equals(name)) {
            if(isEmpty(cellContent)) {
                return new Byte("0");
            } else {
                return Byte.parseByte(cellContent);
            }
        }
        if(fieldTypeClass == Character.class || "char".equals(name)) {
            if(isEmpty(cellContent)) {
                char result = 0;
                return result;
            } else {
                return cellContent.charAt(0);
            }
        }
        if(fieldTypeClass == Boolean.class || "boolean".equals(name)) {
            if(isEmpty(cellContent)) {
                return false;
            } else {
                return Boolean.parseBoolean(cellContent);
            }
        }
        if(isEmpty(cellContent)) {
            return null;
        }
        if(fieldTypeClass == BigDecimal.class) {
            return new BigDecimal(cellContent);
        }
        if(fieldTypeClass == Date.class) {
            int i1 = 0;
            for(int i = 0; DATEPATTERN != null && i < DATEPATTERN.length; i ++) {
                format.applyPattern(DATEPATTERN[i]);
                try {
                    Date parse = format.parse(cellContent);
                    return parse;
                } catch (Exception e) {
                    i1++;
                }
            }
            if(DATEPATTERN != null && i1 == DATEPATTERN.length) {
                throw new Exception("日期不合法");
            }
        }
        return null;
    }

    private static String capitalizeFirstLetter(String original) {
        if (original == null || original.length() == 0) {
            return original;
        }
        return original.substring(0, 1).toUpperCase() + original.substring(1);
    }
    /**
     * Cell 转换为String
     * @param cell
     * @return String
     */
    private static String getCellValue(Cell cell) {
        String ret;

        if(null != cell){
            switch (cell.getCellType().getCode()) {
                case 3:
                    ret = "";
                    break;
                case 4:
                    ret = String.valueOf(cell.getBooleanCellValue());
                    break;
                case 5:
                    ret = "";
                    break;
                case 2:
                    Workbook wb = cell.getSheet().getWorkbook();
                    CreationHelper crateHelper = wb.getCreationHelper();
                    FormulaEvaluator evaluator = crateHelper.createFormulaEvaluator();
                    ret = getCellValue(evaluator.evaluateInCell(cell));
                    break;
                case 0:
                    if (DateUtil.isCellDateFormatted(cell)) {// 处理日期格式、时间格式
                        SimpleDateFormat sdf = null;
                        if (cell.getCellStyle().getDataFormat() == HSSFDataFormat
                                .getBuiltinFormat("h:mm")) {
                            sdf = new SimpleDateFormat("HH:mm");
                        } else {// 日期
                            sdf = new SimpleDateFormat("yyyy-MM-dd");
                        }
                        Date date = cell.getDateCellValue();
                        ret = sdf.format(date);
                    } else if (cell.getCellStyle().getDataFormat() == 58) {
                        // 处理自定义日期格式:m月d日(通过判断单元格的格式id解决,id的值是58)
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
                        double value = cell.getNumericCellValue();
                        Date date = org.apache.poi.ss.usermodel.DateUtil
                                .getJavaDate(value);
                        ret = sdf.format(date);
                    } else {
                        ret = NumberToTextConverter.toText(cell.getNumericCellValue());
//                          double value = cell.getNumericCellValue();
//                          CellStyle style = cell.getCellStyle();
//                          DecimalFormat format = new DecimalFormat();
//                          String temp = style.getDataFormatString();
//                          // 单元格设置成常规
//                          if (temp.equals("General")) {
//                              format.applyPattern("#");
//                          }
//                          ret = format.format(value);
                    }
                    break;
                case 1:
                    ret = cell.getRichStringCellValue().getString();
                    break;
                default:
                    ret = "";
            }
        }else{
            ret = "";
        }

        return ret.trim();
    }
}

然后是测试, 先新建一个测试excel

 导入实体:

package com.example.demo.structs;

import com.example.demo.utils.Import;
import lombok.Data;

import java.math.BigDecimal;
import java.util.Date;

@Data
public class PoiImportAnnotationTest {
    @Import(name="测试列1") String testColumn1;
    @Import(name="测试列2") String testColumn2;
    @Import(name="测试列3") String testColumn3;
    @Import(name="测试列4") String testColumn4;
    @Import(name="测试列5") String testColumn5;
    @Import(name="number字段1") Integer number1;
    @Import(name="number字段2") Integer number2;
    @Import(name="date日期1") Date date1;
    @Import(name="date日期2") Date date2;
    @Import(name="bigdecimal1") BigDecimal bigDecimal1;
    @Import(name="bigdecimal2") BigDecimal bigDecimal2;
}

 

 运行:

 

 

工具中有两个excelToList方法, 其中 XSSFWorkbook是07版本的xlsx, HSSFWorkbook是03版本的xls。 

posted @ 2021-12-31 14:32  dianx  阅读(539)  评论(0)    收藏  举报