当前位置:   article > 正文

java导入功能,直接复制使用,有详细注释

java导入


前言

基于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<T>
     * @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执行结果

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


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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/88893
推荐阅读
相关标签
  

闽ICP备14008679号