• JAVA使用hutool poi工具读取Excel表格指定行列范围的数据


    1.pom.xml依赖配置

            <!-- huTool工具箱 -->
            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>5.7.19</version>
            </dependency>
    
            <!-- poi -->
            <dependency>
                <groupId>org.apache.poi</groupId>
                <artifactId>poi-ooxml</artifactId>
                <version>5.1.0</version>
            </dependency>

    2.代码

    单元格数据模型对象

    package com.hdwang.exceltest;
    
    import cn.hutool.json.JSONUtil;
    
    /**
     * 单元格数据
     */
    public class CellData {
    
        /**
         * 行号
         */
        private int rowIndex;
    
        /**
         * 列号
         */
        private int cellIndex;
    
        /**
         * 单元格数值
         */
        private Object value;
    
        public int getRowIndex() {
            return rowIndex;
        }
    
        public void setRowIndex(int rowIndex) {
            this.rowIndex = rowIndex;
        }
    
        public int getCellIndex() {
            return cellIndex;
        }
    
        public void setCellIndex(int cellIndex) {
            this.cellIndex = cellIndex;
        }
    
        public Object getValue() {
            return value;
        }
    
        public void setValue(Object value) {
            this.value = value;
        }
    
        @Override
        public String toString() {
            return JSONUtil.toJsonStr(this);
        }
    }

    Bean对象属性赋值注解

    package com.hdwang.exceltest;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 列号,用于给bean对象的属性赋指定列的值
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target(value = {ElementType.FIELD})
    public @interface ColIndex {
    
        /**
         * 列索引号(从0开始),与name二者填一个即可,优先级高于name
         *
         * @return
         */
        int index() default -1;
    
        /**
         * 列名称(从A开始),与index二者填一个即可,优先级低于index
         *
         * @return
         */
        String name() default "";
    }

    表格数据对象

    package com.hdwang.exceltest;
    
    import cn.hutool.json.JSONUtil;
    
    /**
     * 证券月报
     */
    public class ZhenquanReport {
    
        /**
         * 名称
         */
        @ColIndex(name = "B")
        private String name;
    
        /**
         * 数值
         */
        @ColIndex(index = 2)
        private String value;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getValue() {
            return value;
        }
    
        public void setValue(String value) {
            this.value = value;
        }
    
        @Override
        public String toString() {
            return JSONUtil.toJsonStr(this);
        }
    }

    读取方法代码

    package com.hdwang.exceltest;
    
    import cn.hutool.poi.excel.ExcelReader;
    import cn.hutool.poi.excel.ExcelUtil;
    import cn.hutool.poi.excel.ExcelWriter;
    import cn.hutool.poi.excel.cell.CellHandler;
    import org.apache.commons.collections4.CollectionUtils;
    import org.apache.poi.ss.usermodel.*;
    
    import java.io.File;
    import java.lang.reflect.Field;
    import java.util.*;
    import java.util.concurrent.atomic.AtomicInteger;
    
    public class Main {
        public static void main(String[] args) {
            try {
                File templateFile = new File("C:\\Users\\hdwang\\Desktop\\test.xlsx");
                List<List<CellData>> rowDataList = readExcelData(templateFile, 2, Integer.MAX_VALUE, 1, Integer.MAX_VALUE);
                System.out.println(rowDataList);
    
                List<ZhenquanReport> reports = convertExcelDataToBeanList(rowDataList, ZhenquanReport.class);
                System.out.println(reports);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    /**
         * 读取表格数据
         *
         * @param templateFile   文件
         * @param startRowIndex  起始行号(从0开始)
         * @param endRowIndex    结束行号(从0开始)
         * @param startCellIndex 起始列号(从0开始)
         * @param endCellIndex   结束列号(从0开始)
         * @return 表格数据
         */
        private static List<List<CellData>> readExcelData(File templateFile, int startRowIndex, int endRowIndex, int startCellIndex, int endCellIndex) {
            ExcelReader excelReader = ExcelUtil.getReader(templateFile, 0);
            List<List<CellData>> rowDataList = new ArrayList<>();
            AtomicInteger rowIndex = new AtomicInteger(-1);
            excelReader.read(startRowIndex, endRowIndex, new CellHandler() {
                @Override
                public void handle(Cell cell, Object value) {
                    if (cell == null) {
                        //无单元格跳过
                        return;
                    }
                    if (cell.getColumnIndex() < startCellIndex || cell.getColumnIndex() > endCellIndex) {
                        //列号不在范围内跳过
                        return;
                    }
    
                    //新行的数据
                    if (cell.getRowIndex() != rowIndex.get()) {
                        rowDataList.add(new ArrayList<>());
                    }
                    rowIndex.set(cell.getRowIndex());
                    //取出新行数据对象存储单元格数据
                    List<CellData> cellDataList = rowDataList.get(rowDataList.size() - 1);
                    CellData cellData = new CellData();
                    cellData.setRowIndex(cell.getRowIndex());
                    cellData.setCellIndex(cell.getColumnIndex());
                    cellData.setValue(value);
                    cellDataList.add(cellData);
                }
            });
            return rowDataList;
        }
    
        /**
         * 转换表格数据为bean对象列表
         *
         * @param rowDataList 表格数据
         * @param tClass      bean类型
         * @param <T>
         * @return bean对象列表
         */
        private static <T> List<T> convertExcelDataToBeanList(List<List<CellData>> rowDataList, Class<T> tClass) {
            if (CollectionUtils.isEmpty(rowDataList)) {
                return new ArrayList<>();
            }
            List<T> beanList = new ArrayList<>();
            for (List<CellData> rowData : rowDataList) {
                try {
                    //实例化bean对象
                    T bean = tClass.newInstance();
                    //遍历字段并赋值
                    Field[] fields = tClass.getDeclaredFields();
                    for (Field field : fields) {
                        if (field.isAnnotationPresent(ColIndex.class)) {
                            ColIndex colIndex = field.getAnnotation(ColIndex.class);
                            int index = colIndex.index();
                            String name = colIndex.name();
                            if (index != -1) {
                                //do nothing
                            } else if (!"".equals(name)) {
                                //列名转索引号(补0为了适应下述方法)
                                index = ExcelUtil.colNameToIndex(name + "0");
                            } else {
                                throw new RuntimeException("请设置列号(ColIndex注解值必须配置一个)");
                            }
                            //从行数据中找到指定单元格数据给字段赋值
                            final int i = index;
                            CellData cellData = rowData.stream().filter(x -> x.getCellIndex() == i).findFirst().orElse(null);
                            if (cellData != null) {
                                Object value = cellData.getValue();
                                if (field.getType().getName().equals(String.class.getName())) {
                                    value = String.valueOf(value);
                                }
                                field.setAccessible(true);
                                field.set(bean, value);
                            }
                        }
                    }
                    beanList.add(bean);
                } catch (Exception ex) {
                    throw new RuntimeException("实例化对象失败", ex);
                }
            }
            return beanList;
        }
    }

    3.表格

    4.输出结果

    表格数据对象

    [
        [
            {
                "cellIndex": 1,
                "rowIndex": 2,
                "value": "净资产"
            },
            {
                "cellIndex": 2,
                "rowIndex": 2,
                "value": 10000
            },
            {
                "cellIndex": 3,
                "rowIndex": 2,
                "value": " "
            },
            {
                "cellIndex": 4,
                "rowIndex": 2,
                "value": 1
            }
        ],
        [
            {
                "cellIndex": 1,
                "rowIndex": 3,
                "value": "市值"
            },
            {
                "cellIndex": 2,
                "rowIndex": 3,
                "value": 20000
            },
            {
                "cellIndex": 4,
                "rowIndex": 3,
                "value": 2
            }
        ],
        [
            {
                "cellIndex": 1,
                "rowIndex": 4,
                "value": "标题"
            }
        ],
        [
            {
                "cellIndex": 1,
                "rowIndex": 5,
                "value": "净利润"
            },
            {
                "cellIndex": 2,
                "rowIndex": 5,
                "value": 1000
            },
            {
                "cellIndex": 4,
                "rowIndex": 5,
                "value": 3
            }
        ]
    ]

     转成bean对象列表

    [{"name":"净资产","value":"10000"}, {"name":"市值","value":"20000"}, {"name":"标题"}, {"name":"净利润","value":"1000"}]

    5.原理总结

    hutool poi 工具对POI进行了包装,实现了很多功能函数。可是在项目实践中发现还是有所不足。现在自己编写自定义取行列范围的数据并实现模型转换功能,从而方便对表格实现数据读取与单元格定位操作等。相关实现技术原理如下:

    (1) 基于 public void read(int startRowIndex, int endRowIndex, CellHandler cellHandler)  函数实现行过滤,在CellHandler内部实现列过滤,这样便实现了行列过滤,且取出了行列信息,便于对单元格定位等。

    (2) 将CellData转换为Bean对象的时候,采用反射技术读取bean对象字段的注解配置的列号信息,从而找到指定的CellData取值并赋值到字段上去。

    6.附录

    最新源码文件:https://github.com/hdwang123/exceltest

  • 相关阅读:
    JavaScript常见注意点(一)
    jspServlet2.5和Servlet3的区别
    jspMVC案例
    jQuery入口函数的写法
    Servlet 简介
    jspMVC设计模式和Servlet2.5入门案例
    display 属性
    JSON简单使用
    Tomcat修改端口号
    php开发环境简单配置
  • 原文地址:https://www.cnblogs.com/hdwang/p/15774601.html
Copyright © 2020-2023  润新知