Octopus——excel导入导出工具
Octopus
Octopus
是一个简易的Excel导入导出工具。目前主要就两个功能:
- 导入:将excel中一行数据转换为指定的java对象,并通过指定的正则表达式检查合法性。
- 导出:按照给定的xml配置,将一个Collection对象写入到excel中。
项目地址:Octopus in github
相关依赖
- poi 3.16
- lombok 1.16.14
- jackson 2.8.6
导入
假设我们需要导入一些学生信息,手上有一个excel文件(在项目src/test/java/resources下的test.xlsx):
studentId name sex inTime score
-----------------------------------------------
20134123 John M 2013-9-1 89
20124524 F 20122-81-31 79
20156243 Joyce 2 2012-5-15 qwe
20116522 Nemo F
先看一下Student
类。
@Getter
@Setter
@ToString
public class Student {
@ModelLineNumber
private int lineNum;
@ModelProperty(value = "id")
private String studentId;
@ModelProperty(value = "name",defaultValue = "anonymous")
private String name;
@ModelProperty(value = "sex",wrongMsg = "sex must be M or F",pattern = "^M|F$")
private String sex;
@ModelProperty(value = "admission",wrongMsg = "admission must be a date")
private LocalDate inTime;
@ModelProperty(value = "score",wrongMsg = "score must be numberic",defaultValue = "100")
private Double score;
}
我们的目标就是把这四行数据转换成四个Student
对象,并且能简单检查一下数据合法性。
再把目光放到Student
类,lineNum
表示该对象对应的row在excel中的行号,需要用@ModelLineNumber
来告诉Octopus
。被@ModelIgnore
标记的属性不会被Octopus
处理,但是即使没有任何注解的属性依然会被处理。利用@ModelProperty
可以自定义更多的信息,这里说一下@ModelProperty
的属性。
- value 该属性的现实含义。
- defaultValue 当excel中没有数据时该属性的默认值。
- blankable excel中的单元格能否为空白。
- wrongMsg 错误信息,给用户作提示作用,通过
ExcelImportException
获取,后面会说到异常处理。 - pattern 检查数据合法性的正则表达式,这里不匹配时也会在异常里有所体现。
现在,我们使用SheetReader
来读取这个excel内容,并转换成四个学生对象。在此之前,先用POI读取这个excel文件,拿到sheet对象
InputStream is = getClass().getResourceAsStream("/test.xlsx");
Workbook workbook = WorkbookFactory.create(is);
Sheet sheet = workbook.getSheetAt(0);
//从索引为1的row,索引为0的col(跟POI一样)开始读取,转换为Student对象
SheetReader<ModelEntity<Student>> students = new ReusableSheetReader<>(sheet,1,0,Student.class);
最后得到一个SheetReader
对象,SheetReader
接口继承了Iterable
,因此可以通过foreach来迭代读取学生信息。
for (ModelEntity<Student> student:students) {
System.out.println(student.getEntity().toString());
}
输出结果如下
Student(lineNum=2, studentId=20134123, name=John, sex=M, inTime=2013-09-01, score=89.0)
Student(lineNum=3, studentId=20124524, name=Joyce, sex=F, inTime=null, score=79.0)
Student(lineNum=4, studentId=20156243, name=anonymous, sex=null, inTime=2015-05-15, score=94.0)
Student(lineNum=5, studentId=20116522, name=Nemo, sex=F, inTime=2011-02-26, score=100.0)
现在关注下异常情况,ModelEntity
除了指定的entity外,还有一个异常集合。
SimpleModelEntity(entity=Student(lineNum=2, studentId=20134123, name=John, sex=M, inTime=2013-09-01, score=89.0), exceptions=[])
SimpleModelEntity(entity=Student(lineNum=3, studentId=20124524, name=Joyce, sex=F, inTime=null, score=79.0), exceptions=[cn.chenhuanming.octopus.exception.DataFormatException: in cell (3,4) ,20123-8-31 can not be formatted to class java.time.LocalDate])
SimpleModelEntity(entity=Student(lineNum=4, studentId=20156243, name=anonymous, sex=null, inTime=2015-05-15, score=94.0), exceptions=[cn.chenhuanming.octopus.exception.PatternNotMatchException: P and ^M|F$ don't match!])
SimpleModelEntity(entity=Student(lineNum=5, studentId=20116522, name=Nemo, sex=F, inTime=2011-02-26, score=100.0), exceptions=[])
可以看到第二个叫Joyce的学生的inTime不是一个日期,因此在异常中有一个DataFormatException
。第三个学生的sex因为不是M或F而有PatternNotMatchException
,同时name是anonymous(默认值)。ModelEntity
里exceptions的元素类型都是ExcelImportException
。
需要注意的是,这里的exceptions只是给程序员提示而已。如果需要返回错误提示给用户的话,可以在ExcelImportException
里获取@ModelProperty
设定的wrongMsg,或者根据异常类的类型来自定义错误提示。
另外ModelEntity
里的entity,即自己指定的pojo,一般只做临时存储,尤其注意的是ReusableSheetReader
这个会重用entity,即迭代过程只有一个entity对象。如果需要每次迭代都要一个全新的对象,可以使用SimpleSheetReader
。
上面例子可以通过执行src/test/java/cn/chenhuanming/octopus/core下的RowAssemblerSheetReaderTest
类来查看运行效果。
导出Excel
导出Excel比导入用起来相对容易一点。但是考虑到可能导出需要自定义的excel内容更多,所以我只做了一个简单的表头绘制和数据填充。
同样的,我们用Student
来做例子,不过这次加多一个班级属性。
@Getter
@Setter
@NoArgsConstructor
@ToString
public class Student {
@ModelLineNumber
private int lineNum;
@ModelProperty(value = "student's id")
private String studentId;
@ModelProperty(value = "student's name",defaultValue = "anonymous")
private String name;
@ModelProperty(value = "student's sex",wrongMsg = "sex must be M or F",pattern = "^M|F$")
private String sex;
@JsonFormat(pattern = "yyyy-MM-dd")
@ModelProperty(value = "student's admission",wrongMsg = "admission must be a date")
private LocalDate inTime;
@ModelProperty(value = "student's score",wrongMsg = "score must be numeric",defaultValue = "100")
private Double score;
@ModelIgnore
private GradeAndClazz gradeAndClazz;
public Student(String studentId, String name, String sex, LocalDate inTime, Double score,GradeAndClazz gradeAndClazz) {
this.studentId = studentId;
this.name = name;
this.sex = sex;
this.inTime = inTime;
this.score = score;
this.gradeAndClazz = gradeAndClazz;
}
}
GradeAndClazz
有两个字符串属性,grade和clazz(因为class是关键字),分别代表年级和班级。
导出我们需要用到一个xml配置文件,让Octopus
了解导出策略。
<?xml version="1.0" encoding="UTF-8"?>
<ExportModel class="entity.Student">
<Field name="studentId" description="id"></Field>
<Field name="name" description="name"></Field>
<Field name="sex" description="sex"></Field>
<Field name="inTime" description="admission"></Field>
<Field name="score" description="score"></Field>
<Field name="gradeAndClazz" description="class info">
<Field name="grade" description="grade"></Field>
<Field name="clazz" description="class"></Field>
</Field>
</ExportModel>
其中的class属性指定了导出用的模版类,每一个标签代表了模版类的属性,这里的顺序就对应了导出顺序,name是模版类中属性的名字,description会用来做表格头。
现在,可以开始编写导出代码了。导出用ExcelWriter
,这里用它的一个实现OneSheetExcelWriter
。
Workbook workbook = new XSSFWorkbook();
String rootPath = this.getClass().getClassLoader().getResource("").getPath();
FileOutputStream os = new FileOutputStream(rootPath+"/export.xlsx");
GradeAndClazz gradeAndClazz = new GradeAndClazz("2014","R6");
Student student1 = new Student("201223","John","M", LocalDate.now(),98.00,gradeAndClazz);
Student student2 = new Student("204354","Tony","M", LocalDate.now(),87.00,gradeAndClazz);
Student student3 = new Student("202432","Joyce","F", LocalDate.now(),90.00,gradeAndClazz);
//指定studentExport.xml映射配置文件,这一步建议在项目中提前初始化并把该对象设成单例。
ExcelWriter<Student> studentExcelWriter = new OneSheetExcelWriter<>(getClass().getClassLoader().getResourceAsStream("studentOutput.xml"));
//往Excel中写入这三个Student
studentExcelWriter.write(workbook,Arrays.asList(student1,student2,student3));
写入excel结果。
| class info |
id name M admission score |---------|----------|
| grade | class |
---------------------------------------------------------------|
201223 John M 2017-07-06 98.0 | 2014 | R6 |
204354 Tony M 2017-07-06 87.0 | 2014 | R6 |
202432 Joyce F 2017-07-06 90.0 | 2014 | R6 |
实际效果请运行src/test/java/cn/chenhuanming/octopus/core/OneSheetExcelWriterTest类。
关于Octopus
的导出原理
导入基本就是获取,检查,赋值,没什么特别的东西。导出虽然看起来比导入简单,但是考虑到自定义输出的可能性更大,由此我想到了可以利用json序列化工具来帮我做对象的转换工作,尤其是这些json序列化工具都会建立一棵树的结构。所以利用这一点,Octopus
本身并不做数据的序列化工作,由jackson转换后给Octopus
一个Node
,Octopus
就可以根据配置文件,通过遍历这棵树来写入excel。因此前一段序列化工作,完全可以利用jackson的一些注解或者@JsonSerialize
等自定义序列化。
Octopus——excel导入导出工具的更多相关文章
- java简易excel导入导出工具(封装POI)
Octopus 如何导入excel 如何导出excel github项目地址 Octopus Octopus 是一个简单的java excel导入导出工具. 如何导入excel 下面是一个excel文 ...
- 一个基于POI的通用excel导入导出工具类的简单实现及使用方法
前言: 最近PM来了一个需求,简单来说就是在录入数据时一条一条插入到系统显得非常麻烦,让我实现一个直接通过excel导入的方法一次性录入所有数据.网上关于excel导入导出的例子很多,但大多相互借鉴. ...
- Java基础学习总结(49)——Excel导入导出工具类
在项目的pom文件中引入 <dependency> <groupId>net.sourceforge.jexcelapi</groupId> <artifac ...
- Excel导入导出工具(简单、好用且轻量级的海量Excel文件导入导出解决方案.)
Excel导入导出工具(简单.好用且轻量级的海量Excel文件导入导出解决方案.) 置顶 2019-09-07 16:47:10 $9420 阅读数 261更多 分类专栏: java 版权声明:本 ...
- java中excel导入\导出工具类
1.导入工具 package com.linrain.jcs.test; import jxl.Cell; import jxl.Sheet; import jxl.Workbook; import ...
- Excel导入导出工具——POI XSSF的使用
工具简介 POI是Apache提供的一款用于处理Microsoft Office的插件,它可以读写Excel.Word.PowerPoint.Visio等格式的文件. 其中XSSF是poi对Excel ...
- java Excel导入导出工具类
本文章,导入导出依赖提前定义好的模板 package com.shareworx.yjwy.utils; import java.io.File; import java.io.FileInputSt ...
- 【原创】POI操作Excel导入导出工具类ExcelUtil
关于本类线程安全性的解释: 多数工具方法不涉及共享变量问题,至于添加合并单元格方法addMergeArea,使用ThreadLocal变量存储合并数据,ThreadLocal内部借用Thread.Th ...
- 有史以来功能最全,使用最简单的excel导入/导出工具
Github地址:https://github.com/xuanbg/Utility. 还有其他一些福利,请各位园友自取. 构造方法 1.用于导出Excel文件 NpoiHelper(ExcelVer ...
随机推荐
- .nomedia文件的作用
.nomedia文件的作用:应用中的图片不被系统图库扫描. 一般开发的应用中会缓存一些图片到本地,不想让系统图库扫描到应用的图片或者不想对用户浏览图片造成影响. .nomedia文件放在任何一个文件夹 ...
- 侯捷STL学习(一)
开始跟着<STL源码剖析>的作者侯捷真人视频,学习STL,了解STL背后的真实故事! 视频链接:侯捷STL 还有很大其他视频需要的留言 第一节:STL版本和重要资源 STL和标准库的区别 ...
- 【小练习02】CSS--网易产品
要求用css和HTML实现下图效果: 代码: <!DOCTYPE html> <html> <head> <meta charset="UTF-8& ...
- Gradle入门学习---认识buildeTypes和dependencies
Gradle是Android Studio默认的构建工具,如果是基本的APP开发,不会涉及到Gradle太多内容,毕竟它的诞生就不是专为Android服务的. 日常开发需要涉及到使用Gradle的场景 ...
- 基于java.util.logging实现轻量级日志记录库(增加根据当前类class初始化,修复线程池模型(javaEE)下的堆栈轨迹顺序与当前调用方法不一致问题)
前言: 本章介绍自己写的基于java.util.logging的轻量级日志记录库(baseLog). 该版本的日志记录库犹如其名,baseLog,是个实现日志记录基本功能的小库,适合小型项目使用,方便 ...
- Canvas学习系列二:Canvas的坐标系统
上一章内容中我们对canvas元素有了一个初步的认识,在接下来的章节中我们会慢慢学习canvas中图形的绘制:但是在绘制之前我们先来看看canvas中的坐标系统,因为这样我们才能知道绘制的图形放在什么 ...
- CSS 简单了解(二)
我们第一天说了简单的HTML,第二天说了简单的CSS.那么今天.咱们就来说一说他们的结合如何使用吧! 首先说引用方式,和使用方法吧! 1.内部样式表.(放入<head>中) <hea ...
- Android 图片加载框架Glide4.0源码完全解析(二)
写在之前 上一篇博文写的是Android 图片加载框架Glide4.0源码完全解析(一),主要分析了Glide4.0源码中的with方法和load方法,原本打算是一起发布的,但是由于into方法复杂性 ...
- C# servicestack.redis 互通 java jedis
拥抱变化,如今也走上了.net/java通吃的时代,下面就讲讲如何让.net/java都能正常访问分片的redis吧. 有几个关键点:一致性环哈希.哈希算法.序列化.反序列化 后两个都比较直接,只要选 ...
- 使用ConfuserEx加密混淆程序以及如何脱壳反编译
一,准备如下工具: ConfuserEx.UnConfuserEx.Fixer.ConfuserExStringDecryptor.ConfuserExSwitchKiller.de4dot.ILSp ...