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

  • 前言
  • 一、引入依赖
  • 二、封装的工具类以及注解类直接copy使用
    • 首先是工具类无需做操作
    • 然后是封装的两个注解类,也是直接复制使用
  • 测试工具类功能
    • 测试实体类
    • Controller层调用
  • 执行结果
    • 如果ifNull 设置为true执行结果
  • 仰天大笑出门去,我辈岂是蓬蒿人

前言

本文基于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>

二、封装的工具类以及注解类直接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开始,所以要减1int 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);// 默认读取第一个sheetSheet 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 blockif (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();}}}

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

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;
}
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 "";}

测试工具类功能

测试实体类

需要注意注解里面的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;
}

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);}}

执行结果

如果ifNull 设置为true执行结果



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

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

  1. 使用回调方式写POI导入excel工具类

    场景是这样的:为了做一个excel导入的功能,为了尽可能的写一个通用的工具类,将与poi有关的东西都封装起来,以便以其他人员只用关心自己的业务,不用和poi打交道. 写到最后,现在还是会有poi的东西 ...

  2. POI导入导出工具类

    导入POI依赖 <dependency><groupId>org.apache.poi</groupId><artifactId>poi</art ...

  3. 一个基于POI的通用excel导入导出工具类的简单实现及使用方法

    前言: 最近PM来了一个需求,简单来说就是在录入数据时一条一条插入到系统显得非常麻烦,让我实现一个直接通过excel导入的方法一次性录入所有数据.网上关于excel导入导出的例子很多,但大多相互借鉴. ...

  4. Java操作百万数据量Excel导入导出工具类(程序代码教程)

    Java操作百万数据量Excel导入导出工具类(程序代码教程): # 功能实现1.自定义导入数据格式,支持配置时间.小数点类型(支持单/多sheet)(2种方式:本地文件路径导入(只支持xls.xls ...

  5. java Excel导入导出工具类 及使用demo

    java Excel导入导出工具类 及使用demo 前言:相信进来的都是想尽快解决问题的,话不多说,按照以下步骤来,可以操作导出excel到本地,导入同理,自行学习.步骤一:直接复制以下excel工具 ...

  6. Java导入Excel工具类使用教程

    前言: 本工具类提供了Excel导入功能,通过反射机制将Excel中数据映射到实体类中,从而获取Excel数据,工具类依赖org.apache.poi包.支持RESTful API,支持Spring ...

  7. 自定义POI的excel工具类-xls-xlsx

    自定义POI的excel工具类-xls-xlsx 使用jdk8(java8)实现Excel导出,随意切换 xls和xlsx.自己只是封装了比较常用方法,07版生成还有bug.ε=(´ο`*)))唉 j ...

  8. php获得帮助类数据_PHP解析xml格式数据工具类示例

    本文实例讲述了PHP解析xml格式数据工具类.分享给大家供大家参考,具体如下: class ome_xml { /** * xml资源 * * @var resource * @see xml_par ...

  9. php解析xml数据格式,PHP解析xml格式数据工具类实例分享

    本文主要介绍了PHP解析xml格式数据工具类,涉及php针对xml格式数据节点添加.获取.解析等相关操作技巧,需要的朋友可以参考下,希望能帮助到大家. 本文实例讲述了PHP解析xml格式数据工具类.分 ...

最新文章

  1. 高校选课成绩管理系统
  2. 英特尔CPU曝出漏洞:监视功耗就能轻松获取数据
  3. wxWidgets:编写非英语应用程序
  4. ArcGIS 10.X功能增减(转)
  5. python应用-pycharm新建模板默认添加shebang编码作者时间等信息
  6. unit 12 文档练习
  7. 计算机信息与自然科学,郑旭飞 - 计算机与信息科学学院 - Powered by 西南大学
  8. redis分布式锁+事务+AOP一起使用注意点
  9. pyserial库是python语言用于,python的pyserial模块
  10. Notepad++下载markdown viewer plugin
  11. 层间距离对ct图像的影响_CT图像影响因素
  12. Python flask web基础(三)用wangeditor实现富文本编辑
  13. win7计算机资源管理器卡住,简单几步解决win7资源管理器老是重启的问题
  14. windows7打印时,显示脱机,提示“服务器打印后台处理程序服务没有运行”。...
  15. 谈谈Gameplay,以及UE4的Gameplay框架
  16. linux性能监控工具-nmon安装使用详细教程
  17. 塑料壳上下扣合的卡扣设计_塑胶件结构设计之止口与扣位的设计
  18. 电脑黑屏按什么键恢复?只需要3个键就可以解决黑屏
  19. malloc申请堆内存
  20. 诺基亚 索爱 低端手机及智能手机 与 QQ邮箱或MyTT 通讯录同步 介绍

热门文章

  1. 数字图像处理——第九章 形态学处理
  2. 使用代码的当前SVN版本构建项目build版号
  3. ceisum添加风场插件
  4. 万能乘法速算法大全_小学数学各年级知识点和重点、难点大全,复习必备提纲!...
  5. Qt之如何读取Excel表格数据
  6. OpenMP学习笔记1
  7. linux的网络管理,Linux之网络管理
  8. cass怎么添加指北针图例_答疑|CASS怎么添加图例?
  9. 2021年 - 年终总结
  10. bugkuctf never give up