• poi导入数据工具类,直接复制使用,有详细注释




    前言

    基于poi、注解类加反射实现自定义文件表头的导入功能、将表头名写入到实体类的注解类上,就可以实现自定义文件的导入功能工具类;

    这是导入的文件数据格式,只读取并导入红色区域的内容
    在这里插入图片描述


    一、引入依赖

    使用到的依赖

        <!-- excel工具 -->
        <dependency>
          <groupId>org.apache.poi</groupId>
          <artifactId>poi</artifactId>
          <version>3.13</version>
        </dependency>
        <dependency>
          <groupId>org.apache.poi</groupId>
          <artifactId>poi-ooxml</artifactId>
          <version>3.13</version>
        </dependency>
        <!--工具类-->
        <dependency>
          <groupId>cn.hutool</groupId>
          <artifactId>hutool-all</artifactId>
          <version>5.7.3</version>
        </dependency>
        <!--实体类get set 构造方法注解-->
        <dependency>
          <groupId>org.projectlombok</groupId>
          <artifactId>lombok</artifactId>
          <version>1.18.12</version>
        </dependency>
    
    	<!--常用工具类 -->
        <dependency>
          <groupId>org.apache.commons</groupId>
          <artifactId>commons-lang3</artifactId>
        </dependency>
    
        <!-- io常用工具类 -->
        <dependency>
          <groupId>commons-io</groupId>
          <artifactId>commons-io</artifactId>
          <version>2.11.0</version>
        </dependency>
    
        <!-- 文件上传工具类 -->
        <dependency>
          <groupId>commons-fileupload</groupId>
          <artifactId>commons-fileupload</artifactId>
          <version>1.4</version>
        </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    二、封装的工具类以及注解类直接copy使用

    一共需要三个java文件,工具类和两个注解类

    首先是工具类无需做操作

    import cn.hutool.core.date.DateUtil;
    import cn.hutool.core.util.ObjectUtil;
    import cn.hutool.core.util.StrUtil;
    import com.study.test.util.note.Description;
    import com.study.test.util.note.Excel;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.BooleanUtils;
    import org.apache.commons.lang3.CharUtils;
    import org.apache.commons.lang3.StringUtils;
    import org.apache.commons.lang3.math.NumberUtils;
    import org.apache.poi.hssf.usermodel.HSSFDateUtil;
    import org.apache.poi.hssf.usermodel.HSSFWorkbook;
    import org.apache.poi.ss.usermodel.Cell;
    import org.apache.poi.ss.usermodel.Row;
    import org.apache.poi.ss.usermodel.Sheet;
    import org.apache.poi.ss.usermodel.Workbook;
    import org.apache.poi.xssf.usermodel.XSSFWorkbook;
    import org.springframework.web.multipart.MultipartFile;
    
    import java.io.InputStream;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Field;
    import java.math.BigDecimal;
    import java.text.DecimalFormat;
    import java.util.*;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    
    /**
     * 注释: excel工具类
     *
     * @author yangyongzhuo 2022/11/17 19:59
     */
    @Slf4j
    public class ExcelUtils {
    
        private final static String EXCEL2003 = "xls";
        private final static String EXCEL2007 = "xlsx";
    
        /**
         * 通过clsss,读取excel里面的数据,只要表头与Excel里面的notes一致就可以,不要关注顺序
         *
         * @param file  文件
         * @param clsss vo
         * @return vo集合
         * @return java.util.List
         * @author yangyongzhuo 2022/11/17 19:59
         */
        public static <T> List<T> readExcel(MultipartFile file, Class<T> clsss) {
            //开始执行时间
            long start = System.currentTimeMillis();
    
            Workbook workbook = null;
            //返回数据对象
            List<T> dataList = null;
    
            //先判断文件命名是否正确,再判断文件是哪种类型
            String fileName = file.getName();
            if (!fileName.matches("^.+\\.(?i)(xls)$") && !fileName.matches("^.+\\.(?i)(xlsx)$")) {
                log.error("上传文件格式不正确");
                throw new RuntimeException("Excel命名格式不正确!");
            }
            try {
                InputStream is = file.getInputStream();
                if (fileName.endsWith(EXCEL2007)) {
                    workbook = new XSSFWorkbook(is);
                }
                if (fileName.endsWith(EXCEL2003)) {
                    workbook = new HSSFWorkbook(is);
                }
                if (ObjectUtil.isEmpty(workbook)) {
                    throw new RuntimeException("Excel格式不正确,未获取工作空间!");
                }
    
                dataList = new ArrayList<>();
                //通过反射获取注释类上的数据下标值
                Description annotation = clsss.getAnnotation(Description.class);
                //数据位置因为poi读取下标是从0开始,所以要减1
                int dataIndex = annotation.dataIndex() - 1;
                //是否忽略空行
                boolean ifNull = annotation.ifNull();
    
                // 类映射 注解,拿到文件字段名称
                Map<String, List<Field>> classMap = new HashMap<>();
                List<Field> fields = Stream.of(clsss.getDeclaredFields()).collect(Collectors.toList());
                fields.forEach(field -> {
                    //
                    Excel excel = field.getAnnotation(Excel.class);
                    if (ObjectUtil.isEmpty(excel)) {
                        return;
                    }
                    String notes = excel.notes();
                    if (StrUtil.isEmpty(notes)) {
                        return;
                    }
                    if (!classMap.containsKey(notes)) {
                        classMap.put(notes, new ArrayList<>());
                    }
                    field.setAccessible(true);
                    classMap.get(notes).add(field);
                });
                // 获取字段对应的列号
                Map<Integer, List<Field>> reflectionMap = new HashMap<>(16);
                // 默认读取第一个sheet
                Sheet sheet = workbook.getSheetAt(0);
                //
                boolean firstRow = true;
                for (int i = sheet.getFirstRowNum(); i <= sheet.getLastRowNum(); i++) {
                    Row row = sheet.getRow(i);
                    // 表头区域获取字段对应的列
                    if (i < dataIndex) {
                        for (int j = row.getFirstCellNum(); j <= row.getLastCellNum(); j++) {
                            Cell cell = row.getCell(j);
                            String cellValue = getCellValue(cell);
                            if (classMap.containsKey(cellValue)) {
                                reflectionMap.put(j, classMap.get(cellValue));
                            }
                        }
                        firstRow = false;
                    } else {
                        // 忽略空白行
                        if (row == null) {
                            continue;
                        }
                        try {
                            T t = clsss.newInstance();
                            // 判断是否为空白行
                            boolean allBlank = true;
                            for (int j = row.getFirstCellNum(); j <= row.getLastCellNum(); j++) {
                                if (reflectionMap.containsKey(j)) {
                                    Cell cell = row.getCell(j);
                                    String cellValue = getCellValue(cell);
                                    if (StringUtils.isNotBlank(cellValue)) {
                                        allBlank = false;
                                    }
                                    List<Field> fieldList = reflectionMap.get(j);
                                    fieldList.forEach(x -> {
                                        try {
                                            handleField(t, cellValue, x);
                                        } catch (Exception e) {
                                            log.error(String.format("reflect field:%s value:%s exception!", x.getName(),
                                                    cellValue), e);
                                        }
                                    });
                                }
                            }
                            if (!allBlank) {
                                dataList.add(t);
                            } else {
                                //if is null return this import code block
                                if (ifNull) {
                                    return dataList;
                                }
                                log.warn(String.format("row:%s is blank ignore!", i));
                            }
                        } catch (Exception e) {
                            log.error(String.format("parse row:%s exception!", i), e);
                        }
                    }
                }
            } catch (Exception e) {
                log.error(String.format("parse excel exception!"), e);
            } finally {
                if (workbook != null) {
                    try {
                        workbook.close();
                    } catch (Exception e) {
                        log.error(String.format("parse excel exception!"), e);
                    }
                }
            }
            long end = System.currentTimeMillis();
            log.info("read excel cost {}s", (end - start) / 1000);
            return dataList;
        }
    
        /**
         * 注释: 获取数据原始类型
         *
         * @param t
         * @param value
         * @param field
         * @return void
         * @author yangyongzhuo 2022/11/25 13:25
         */
        private static <T> void handleField(T t, String value, Field field) throws Exception {
            Class<?> type = field.getType();
            if (type == null || type == void.class || StringUtils.isBlank(value)) {
                return;
            }
            if (type == Object.class) {
                field.set(t, value);
                // 数字类型
            } else if (type.getSuperclass() == null || type.getSuperclass() == Number.class) {
                if (type == int.class || type == Integer.class) {
                    field.set(t, NumberUtils.toInt(value));
                } else if (type == long.class || type == Long.class) {
                    field.set(t, NumberUtils.toLong(value));
                } else if (type == byte.class || type == Byte.class) {
                    field.set(t, NumberUtils.toByte(value));
                } else if (type == short.class || type == Short.class) {
                    field.set(t, NumberUtils.toShort(value));
                } else if (type == double.class || type == Double.class) {
                    field.set(t, NumberUtils.toDouble(value));
                } else if (type == float.class || type == Float.class) {
                    field.set(t, NumberUtils.toFloat(value));
                } else if (type == char.class || type == Character.class) {
                    field.set(t, CharUtils.toChar(value));
                } else if (type == boolean.class) {
                    field.set(t, BooleanUtils.toBoolean(value));
                } else if (type == BigDecimal.class) {
                    field.set(t, new BigDecimal(value));
                }
            } else if (type == Boolean.class) {
                field.set(t, BooleanUtils.toBoolean(value));
            } else if (type == Date.class) {
                field.set(t, value);
            } else if (type == String.class) {
                field.set(t, value);
            } else {
                Constructor<?> constructor = type.getConstructor(String.class);
                field.set(t, constructor.newInstance(value));
            }
        }
    
        /**
         * 注释:  获取数据类型
         *
         * @param cell
         * @return java.lang.String
         * @author yangyongzhuo 2022/11/25 13:26
         */
        private static String getCellValue(Cell cell) {
            if (cell == null) {
                return "";
            }
    
            int cellType = cell.getCellType();
            if (cellType == Cell.CELL_TYPE_FORMULA) { // 表达式类型
                cellType = cell.getCachedFormulaResultType();
            }
    
            if (cellType == Cell.CELL_TYPE_NUMERIC) {
                if (HSSFDateUtil.isCellDateFormatted(cell)) {
                    Date date = HSSFDateUtil.getJavaDate(cell.getNumericCellValue());
                    return DateUtil.format(date, "yyyy-MM-dd");
                } else {
                    return new DecimalFormat("#.######").format(cell.getNumericCellValue());
                }
            } else if (cellType == Cell.CELL_TYPE_STRING) {
                return StringUtils.trimToEmpty(cell.getRichStringCellValue() + "");
            } else if (cellType == Cell.CELL_TYPE_BLANK) {
                return "";
            } else if (cellType == Cell.CELL_TYPE_BOOLEAN) {
                return String.valueOf(cell.getBooleanCellValue());
            } else if (cellType == Cell.CELL_TYPE_ERROR) {
                return "ERROR";
            } else {
                return cell.toString().trim();
            }
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264

    然后是封装的两个注解类,也是直接复制使用

    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 注释: 读取信息
     *
     * @author yangyongzhuo 2022/11/17 15:41
     */
    @Target({ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Description {
    
        String description() default "";
    
        /** 读到空白行处理方式 true 结束此sheet页导入,false继续导入 */
        boolean ifNull() default true;
    
        /** 表头的位置 */
        int headerIndex() default 0;
    
        /** 数据行的下表位置 */
        int dataIndex() default 1;
    
        /** 起始sheet页的下标 */
        int startSheetIndex() default 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * 注释: 备注注解
     *
     * @author yangyongzhuo 2022/11/18 10:56
     */
    @Target({ElementType.METHOD, ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Excel {
    
        String value() default "";
    
        String notes() default "";
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    测试工具类功能

    测试实体类

    需要注意注解里面的notes是你要导入的字段名称,可以参考我的写法,

    /**
     * 注释: 导入对象
     * @Description: dataIndex 数据的行号, 
     * ifNull默认是true 如果有空行就终止导入,false就是忽略空行继续导入
     *
     * @author yangyongzhuo 2022/11/17 15:42
     */
    @Description(dataIndex = 9,ifNull = false)
    @Data
    public class ReadExcelVo {
    
        @Excel(notes = "监测点编号")
        private String param1;
    
        @Excel(notes = "上次累计")
        private String param2;
    
        @Excel(notes = "本次累计")
        private String param3;
    
        @Excel(notes = "本次变化")
        private String param4;
    
        @Excel(notes = "变化速率(mm/d)")
        private String param5;
    
        @Excel(notes = "速率(mm/d)")
        private String param6;
    
        @Excel(notes = "累计值 (mm)")
        private String param7;
    
        @Excel(notes = "对应位置\n" +
                "(区域)")
        private String param8;
    
        @Excel(notes = "备注")
        private String param9;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    Controller层调用

    /**
     * 注释: 文件测试
     *
     * @author yangyongzhuo 2022/11/17 20:01
     */
    @RestController
    @RequestMapping("/test/controller")
    public class TestController {
    
        @PostMapping("/testImport")
        public void testImport(@RequestParam("file") MultipartFile file) {
            List<ReadExcelVo> readExcelVos = ExcelUtils.readExcel(file, ReadExcelVo.class);
            //打印导入数据
            readExcelVos.forEach(System.err::println);
        }
        
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    执行结果

    在这里插入图片描述

    如果ifNull 设置为true执行结果

    在这里插入图片描述
    在这里插入图片描述


    仰天大笑出门去,我辈岂是蓬蒿人

  • 相关阅读:
    我们一起来学习亚马逊云科技EC2实例知识点吧
    PHP代码审计14—变量覆盖
    【python 学习】如何将Python脚本打包成可执行的exe文件#520表白代码
    Mysql ProxySQL的学习
    P7557 [USACO21OPEN] Acowdemia S
    【C】程序环境和预处理
    JuiceFS 目录配额功能设计详解
    青岛大学数据结构与算法——第6章
    【报错】Error: opening registry key ‘Software\JavaSoft\Java Runtime Environment‘
    10分钟部署一套开源表单系统
  • 原文地址:https://blog.csdn.net/weixin_48207312/article/details/127910625