前言

之前写的项目中,有个需求,需要导出导入Excel表格;

本来很简单的一件事,等到具体实现的时候,才发现,楼上部门给的表格很乱;

比如 Sheet A 单元簿中,公司名称在B列,那么 Sheet B 单元簿中,公司列就可能已经在 C 列上,或者直接没有公司列了 ;

或则,有的单元格有数据,有的没有数据,或者日期单元格中写的不是日期,是个字符串;

或者,同一个excel表的数据,来自多个 po 类对象,比如 A 列数据最后要封装到到 公司类 中,B 列数据最后封装到 合同类 里面 。这是最变态的;

总而言之,Excel乱的很,根本没有模板,因为这些Excel数据在写程序之前,已经存在了 ;

而给我的任务,则是导入这些表格,并且帮他们保存到数据库中,还要保持数据之间的关系;

现在摆在面前的是:别人是Excel适应程序,我面前的则是写程序兼容Excel ;

思来想去,写一个通用的工具类吧,也方便以后再遇到类似的问题,顺便展示下我实习生的能力哈;


当前支持的功能

工期很紧张,没有那些时间给我慢慢研究,开发导入当初已经用了三四天时间;


但是导出真的很简单,相对于导入来说,看下 poi 文档就可以写出来,以后有时间,再更下。

  1. excel导入到内存中,放在一个Map<String,List<Object>>里面;

    K 是类的全限定名,V 是保存数据的list集合 ;

  2. 在读取数据的时候,可以对数据进行校验,支持正则表达式;

    通过在 po 类的字段上配置注解,进行数据的检验,不配置,则默认匹配任何数据;

  3. 如果数据校验结果有错误,则返回返回一个list集合,里面封装错误信息,便于前台显示;

    错误信息,也是用过注解,自己定义错误信息,默认错误信息为空串 ;


方法api

类名为 MyExcelUtils ,方法为非 static 的,因为方法跑在服务器端,多个浏览器可能并发访问,使用静态方法,可能导致保存错误信息的集合中的错误信息乱掉 ;

  1. 获取检验信息

     /**
    * 获取保存错误信息的集合
    * @return list
    */
    public List<String> getIndexErrors();

    返回一个保存哪行哪列发生错误的信息的集合;如果没有错误信息,则集合的大小为 0

  2. 导入excel表格

    /**
    * 功能:导入 excel表格,将内容保存到对应的对象的集合中 ;
    *
    * @param file 需要导入的 excel 表格
    * @param classes excel 中 数据最后封装到哪些对象中;
    */
    public Map<String, List<Object>> importExcel(File file, List<Class> classes)
    throws IOException,
    InvalidFormatException,
    IllegalAccessException,
    InstantiationException

    File 参数是要到导入的excel表格对象

    List<Class> 参数是excel表格数据来自那些类,里面保持它们的 Class 对象 ;


配置

要想工具正确的工作,还需要做一些配置的;

配置效果如下:

@FromWho(className = "cn.hyc.vo.ContractVo")
public class Contract { @ExcelVOAttribute(name = "合同ID", column = "A", CLASS = Integer.class)
private Integer contId;
@ExcelVOAttribute(name = "项目名字", column = "B")
private String itemName;
....
}
  1. @FromWho

    只有一个 String className() ; 属性,该属性使用在最基类上(Object除外),内容为最子类的全限定名;

    比如有3个类 ,A继承B,B继承C;则该注解写在C类上,里面内容为 A

  2. @ExcelVOAttribute

    其中这个注解,最初来源于我在网上找导入excel的一个工具类,但是那个工具类,写的不完善,,好多东西都没做判断,。。但是也给予我指导了;

    那个地址为:https://blog.csdn.net/lk_blog/article/details/8007777

    /**
    * 导出到Excel中的列的名字.也是能读取识别的重要因子
    */
    String name(); /**
    * 配置列的名称,对应A,B,C,D,导出的时候,想让数据在哪一列,就写对应的列名字
    */
    String column(); /**
    * 该列是什么类型,指定列的数据类型,默认是String 类型
    */
    Class CLASS() default String.class ; /**
    * 使用正则表达式 对数据的内容进行校验。默认匹配任何数据
    */
    String regex() default "(.|\n)*"; /**
    * 错误提示信息,默认为空串
    */
    String info() default "";

如何使用(Demo)

  1. 导入

    方法参数添加的类是最子类;


    List<Class> classes = new ArrayList<>();
    // 将最终结果封装到哪一个类里面,这里就添加谁,可添加多个
    classes.add(Class.forName("cn.hyc.vo.ContractVo"));
    MyExcelUtils myExcelUtils = new MyExcelUtils();
    // 返回excel的数据,封装到各自对应的po对象里面
    Map<String, List<Object>> map = myExcelUtils.importExcel(new File("xxx.xlms"), classes);
  2. 获取错误信息


    List<String> errors = myExcelUtils.getIndexErrors();
    if (errors.size() > 0) {
    sonObject.put("result", "0");
    jsonObject.put("resultInfo", JSONObject.toJSON(errors));
    return jsonObject.toJSONString();
    }
  3. 从Map里面获取自己想要的po类数据‘

    	 List listObject = map.get("cn.hyc.vo.ContractVo");
    if (listObject.size() == 0) {
    continue;
    }
    // 根据类的全限定名获取
    List<ContractVo> list = new ArrayList<>(listObject.size());
    // 进行强转
    for (int j = 0; j < listObject.size(); j++) {
    list.add((ContractVo) listObject.get(j));
    }

实现思路(该工具类可正确的一个大前提)

无论你多个单元簿,里面的列怎么变换,怎么增删,但是你那个列的名字是不变的;(这是大前提,如果没有这个前提,则本工具将只读取和配置 name 的属性一样的列)

比如,公司名称数据那列,无论你一会放在A列,一会放在B列,但是你的那一列名字总归是 公司名称

从这个入手;

比如现在有5列,分类来自Aaa Bbb Ccc三个类。

类名 excel列 — 字段 excel列 — 字段
Aaa A — name B — age
Bbb D — time E — money
Ccc E — nickname

按照上面的分配;去配置Aaa Bbb Ccc三个类 ;

仅演示配置 Aaa

@FromWho(className = "Aaa")
public class Aaa{ @ExcelVOAttribute(name = "姓名", column = "A", CLASS = Integer.class)
private String name;
@ExcelVOAttribute(name = "年龄", column = "B")
private String age;
....
}

工具内部的实现原理就是:先加载那些,传进来的Class对象,通过反射,获取其所有的子段,包括父类的字段,直到最基类;

拿到字段以后,只获取那些标注了特定注解的字段;

然后,获取注解中的内容:

比如加载 Aaa 先获取其头上标注的 @FromWho 的内容,知道这个类的字段上面的列名字都是来自 Aaa ,然后读取 Aaa 的字段,获取使用了 @ExcelVOAttribute 的内容,知道 在 excel姓名 列的内容最后封装到 Aaaname 字段上 ,知道 在 excel年龄 列的内容最后封装到 Aaaage 字段上 ;以此类推

关系绑定代码如下:

/**
* 将配置中配置的类的有注解字段,加载进 map 里面,K-V K是字段 V是注解的值,也就是基类名称 ;
* <p>
* 配置各项映射关系
*
* @param classes
* @return
*/
private void setFieldMapping(List<Class> classes) { Class clazz = null;
for (int i = 0; i < classes.size(); i++) {
clazz = classes.get(i);
int j = 0;
FromWho fromWho = (FromWho) clazz.getAnnotation(FromWho.class);
String className = fromWho.className();
Field[] allFields = clazz.getDeclaredFields(); for (Field field : allFields) {
if (field.isAnnotationPresent(ExcelVOAttribute.class)) {
String column = field.getAnnotation(ExcelVOAttribute.class).column();
String indexName = field.getAnnotation(ExcelVOAttribute.class).name();
String regex = field.getAnnotation(ExcelVOAttribute.class).regex();
String info = field.getAnnotation(ExcelVOAttribute.class).info();
int count = getColumnIndex(column);
cellMaxNum = Math.max(count, cellMaxNum);
indexNameClassMap.put(indexName, className);
indexNameFieldMap.put(indexName, field);
indexNameRegex.put(field, regex);
indexNameErrorInfo.put(field, info);
}
}
if (clazz.getSuperclass() != null
&& !clazz.getSuperclass().equals(Object.class)) {
List<Class> list = new ArrayList<>();
list.add(clazz.getSuperclass());
setFieldMapping(list);
}
}
}

涉及到的关系如下:

   /**
* 将字段进行分拣保持在其中,按照其在excel的列索引;
*/
private Map<Integer, String> indexClassMap = new HashMap<>(); /**
* 字段与列索引之间的关系
*/
private Map<String, Field> indexNameFieldMap = new HashMap<>(); /**
* 保存配置文件的 class 对象
*/
private Map<String, Class> name4class = new HashMap<>(); /**
* 列名字与类的映射关系
*/
private Map<String, String> indexNameClassMap = new HashMap<>(); /**
* 列与校验规则的映射关系
*/
private Map<Field, String> indexNameRegex = new HashMap<>();
/**
* 错误配置信息
*/
private Map<Field, String> indexNameErrorInfo = new HashMap<>();
/**
* 列内容的错误信息
*/
private List<String> indexErrors = new ArrayList<>();
/**
* 最大单元格数
*/
private int cellMaxNum = 0;
/**
* 列索引与字段的映射
*/
private Map<Integer, String> indexFieldMap = new HashMap<>();

后记

具体实现的代码太长了,一个导入 500 多行,就不放上来,我将它们封为一个 jar 包了;

下载地址 :Excel工具类 jar 包

我自己的东西,我竟然不能设置为免费下载,最低 1 积分…

【工具】导入导出 Excel的更多相关文章

  1. 导入导出Excel工具类ExcelUtil

    前言 前段时间做的分布式集成平台项目中,许多模块都用到了导入导出Excel的功能,于是决定封装一个ExcelUtil类,专门用来处理Excel的导入和导出 本项目的持久化层用的是JPA(底层用hibe ...

  2. 导入导出Excel的Java工具类ExcelUtil

    在编写ExcelUtil之前,在网上查了一些资料.java中用来处理Excel的第三方开源项目主要就是POI和JXL.poi功能强大,但是比较耗资源,对于大数据量的导入导出性能不是太好:jxl功能简单 ...

  3. C#中缓存的使用 ajax请求基于restFul的WebApi(post、get、delete、put) 让 .NET 更方便的导入导出 Excel .net core api +swagger(一个简单的入门demo 使用codefirst+mysql) C# 位运算详解 c# 交错数组 c# 数组协变 C# 添加Excel表单控件(Form Controls) C#串口通信程序

    C#中缓存的使用   缓存的概念及优缺点在这里就不多做介绍,主要介绍一下使用的方法. 1.在ASP.NET中页面缓存的使用方法简单,只需要在aspx页的顶部加上一句声明即可:  <%@ Outp ...

  4. Java基于注解和反射导入导出Excel

    代码地址如下:http://www.demodashi.com/demo/11995.html 1. 构建项目 使用Spring Boot快速构建一个Web工程,并导入与操作Excel相关的POI包以 ...

  5. EasyPOI导入导出Excel

    EasyPOI工具可以方便的写出Excel导出,Excel模板导出,Excel导入,Word模板导出,通过简单的注解和模板 导入maven依赖 <dependency> <group ...

  6. ASP.NET Core 导入导出Excel xlsx 文件

    ASP.NET Core 使用EPPlus.Core导入导出Excel xlsx 文件,EPPlus.Core支持Excel 2007/2010 xlsx文件导入导出,可以运行在Windows, Li ...

  7. thinkphp导入导出excel表单数据

    在PHP项目经常要导入导出Excel表单. 先去下载PHPExcel类库文件,放到相应位置. 我在thinkphp框架中的位置为ThinkPHP/Library/Org/Util/ 导入 在页面上传e ...

  8. php中导入导出excel的原理

    在php中我们要经常导入导出excel文件,方便后台管理.那么php导入和导出excel的原理到底是什么呢?excel分为两大版本excel2007(后缀.xlsx).excel2003(后缀.xls ...

  9. NPOI导入导出EXCEL通用类,供参考,可直接使用在WinForm项目中

    以下是NPOI导入导出EXCEL通用类,是在别人的代码上进行优化的,兼容xls与xlsx文件格式,供参考,可直接使用在WinForm项目中,由于XSSFWorkbook类型的Write方法限制,Wri ...

  10. .NET导入导出Excel

    若是开发后台系统,ASP.NET MVC中总是涉及了很多导入导出Excel的问题,有的时候处理起来比较烦 如果能使用以下代码解决,就完美了 public class ReportModel { [Ex ...

随机推荐

  1. Java 中List集合中自定义排序

    /* 集合框架的工具类. Collections:集合框架的工具类.里面定义的都是静态方法. Collections和Collection有什么区别? Collection是集合框架中的一个顶层接口, ...

  2. Android中相对布局的两个控件

    <Button android:id="@+id/button3" android:layout_width="wrap_content" android ...

  3. PHP-FPM远程代码执行漏洞(CVE-2019-11043)

    0x00 简介 在长亭科技举办的 Real World CTF 中,国外安全研究员 Andrew Danau 在解决一道 CTF 题目时发现,向目标服务器 URL 发送 %0a 符号时,服务返回异常, ...

  4. GAN 原理及公式推导

    Generative Adversarial Network,就是大家耳熟能详的 GAN,由 Ian Goodfellow 首先提出,在这两年更是深度学习中最热门的东西,仿佛什么东西都能由 GAN 做 ...

  5. 第06组 团队Git现场编程实战

    一.组员职责分工 队员姓名 主要分工 朱庆章 测评福州最受欢迎的商圈(参考人气) 陈梦雪 测评福州最受欢迎的商圈(参考人气) 关文涛 分别测评福州人均消费50以下,50-100.100-200.200 ...

  6. Git是怎么Ignore文件的?

    Git is one of the most popular version control systems (VCS) available, especially thanks to hosting ...

  7. Install LEDE on a BT Home Hub 5 / Plusnet One Router

    Overview / Purpose of this guide These instructions are for aimed at users of Windows but a lot of t ...

  8. google镜像《转》

    最新谷歌镜像列表 https://jsproxy-demo.ml 谷歌镜像F1http://go.yuxuantech.com 谷歌镜像F1,非SSLhttps://www.siwa88.net 谷歌 ...

  9. Vscode 保存文件就会自动添加注释

    Vscode 保存文件就会自动添加注释   原因是:安装了插件造成的..   文章来源:刘俊涛的博客 欢迎关注公众号.留言.评论,一起学习. _____________________________ ...

  10. 【转】自动化框架中引入ExtentReport美化报告

    本文链接:https://blog.csdn.net/qq_30353203/article/details/82023922一.先引入三个依赖包 <dependency> <gro ...