基于Apache POI的Excel导入导出工具

一个简单的基于Apache POI的Excel导入导出


  • 一个简单的基于Apache POI的Excel导入导出
    • 介绍
    • 实现
    • 源代码下载

介绍

Excel导入导出数据在项目中还是比较常用,这次正好遇到了这个问题,就写了个简单的通用的导入导出工具。

实现

  • Maven依赖
<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源代码

你可能感兴趣的:(Java)