Octopus
(就是一个名字而已~) 是一个简单的java excel导入导出工具。目的是不用接触Apache POI的API就能够完成简单的Excel导出导入。 同时,能够自定义表格样式,导入检验数据合法和转换数据。php
最大的特色就是导出复杂结构对象时自动绘制表头java
不BB,直接上图ios
为了准确性,演示的例子都是完整的单元测试代码,均可以在测试路径找到源代码,各位客官能够clone下来跑一下看效果,包括上面这个复杂例子(ApplicantExample)喔git
Github地址github
<dependency>
<groupId>cn.chenhuanming</groupId>
<artifactId>octopus</artifactId>
<version>最新版本请到github查看</version>
</dependency>
复制代码
咱们从最简单的例子开始——导出一些地址数据。Address
类只有两个属性正则表达式
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Address {
private String city;
private String detail;
}
复制代码
在导出前,咱们须要建立一个XML文件定义怎样去导出bash
<?xml version="1.0" encoding="UTF-8"?>
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/zerouwar/my-maven-repo/master/cn/chenhuanming/octopus/1.1.0/octopus.xsd" class="cn.chenhuanming.octopus.entity.Address">
<Field name="city" description="City"/>
<Field name="detail" description="Detail"/>
</Root>
复制代码
首先,建立Root
根元素。这里引用octopus.xsd文件帮助咱们编写XMLmaven
而后,赋值class
属性,表明咱们要导出的类全限定名ide
最后,建立两个Field
元素,表明要导出类的两个属性工具
name
属性值就是Address
里的属性名,实际上Octopus调用其getter方法获取值,因此要确保有getter方法
description
属性会被用在绘制表头
咱们能够开始作最后一件事,编写Java代码
public class AddressExample {
List<Address> addresses;
/** * make testing data */
@Before
public void prepare() {
addresses = new ArrayList<>();
DataFactory df = new DataFactory();
for (int i = 0; i < df.getNumberBetween(5, 10); i++) {
addresses.add(new Address(df.getCity(), df.getAddress()));
}
}
@Test
public void export() throws FileNotFoundException {
//where to export
String rootPath = this.getClass().getClassLoader().getResource("").getPath();
FileOutputStream os = new FileOutputStream(rootPath + "/address.xlsx");
//read config from address.xml
InputStream is = this.getClass().getClassLoader().getResourceAsStream("address.xml");
ConfigFactory configFactory = Octopus.getXMLConfigFactory(is);
try {
Octopus.writeOneSheet(os, configFactory, "address", addresses);
} catch (IOException e) {
System.out.println("export failed");
}
}
}
复制代码
这是一个完整的单元测试代码,不过导出Excel其实只要两步:
ConfigFactory
对象Octopus.writeOneSheet()
,传入导出的文件,configFactory,工做表的名字和数据下面是导出的Excel文件
Octopus支持导出复杂对象时自动绘制表头
此次咱们来导出一些公司数据,这里是Company
类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Company {
private String name;
private Address address;
}
复制代码
而后咱们建立一个 company.xml 配置文件
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/zerouwar/my-maven-repo/master/cn/chenhuanming/octopus/1.1.0/octopus.xsd" class="cn.chenhuanming.octopus.entity.Address">
<Field name="name" description="Name" color="#ff0000"/>
<Header name="address" description="Address">
<Field name="city" description="City"/>
<Field name="detail" description="Detail"/>
</Header>
</Root>
复制代码
咱们用Header
元素表明要导出Company
的一个复杂属性,同时设置字体颜色是红色
Java代码基本跟以前的同样
public class CompanyExample {
List<Company> companies;
/** * make testing data */
@Before
public void prepare() {
companies = new ArrayList<>();
DataFactory df = new DataFactory();
for (int i = 0; i < df.getNumberBetween(5, 10); i++) {
companies.add(new Company(df.getBusinessName(), new Address(df.getCity(), df.getAddress())));
}
}
@Test
public void export() throws FileNotFoundException {
//where to export
String rootPath = this.getClass().getClassLoader().getResource("").getPath();
FileOutputStream os = new FileOutputStream(rootPath + "/company.xlsx");
//read config from company.xml
InputStream is = this.getClass().getClassLoader().getResourceAsStream("company.xml");
ConfigFactory configFactory = Octopus.getXMLConfigFactory(is);
try {
Octopus.writeOneSheet(os, configFactory, "company", companies);
} catch (IOException e) {
System.out.println("export failed");
}
}
}
复制代码
最后是导出的Excel文件
Octopus能够处理更复杂的数据,你能够在cn.chenhuanming.octopus.example.ApplicantExample
查看这个更复杂的例子
有时你想转换导出的数据。例如,在上一个例子中,咱们不想导出整个Address
对象,把它当作一个字符串导出
咱们所须要作的只是实现一个Formatter
public class AddressFormatter implements Formatter<Address> {
@Override
public String format(Address address) {
return address.getCity() + "," + address.getDetail();
}
@Override
public Address parse(String str) {
String[] split = str.split(",");
if (split.length != 2) {
return null;
}
return new Address(split[0], split[1]);
}
}
复制代码
parse
方法用于导入Excel时,只要关注format
方法。这里接受一个Address对象,返回一个字符串。
最后,配置AddressFormatter
到XML文件
<Field name="name" description="Name" color="#ff0000"/>
<Field name="address" description="Address" formatter="cn.chenhuanming.octopus.formatter.AddressFormatter"/>
复制代码
最后导出的结果
咱们直接拿上一个例子的导出结果来演示导入,共用同一个ConfigFactory
,直接编写导入的代码
//First get the excel file
FileInputStream fis = new FileInputStream(rootPath + "/company2.xlsx");
try {
SheetReader<Company> importData = Octopus.readFirstSheet(fis, configFactory, new DefaultCellPosition(1, 0));
for (Company company : importData) {
System.out.println(company);
}
} catch (Exception e) {
System.out.println("import failed");
}
复制代码
在控制台能够看到打印导入结果,能够看到,以前的AddressFormatter
也完成了数据的转换工做
Company(name=Graham Motor Services, address=Address(city=Monroe, detail=666 Bonnair Ave))
Company(name=Social Circle Engineering, address=Address(city=Fort Gaines, detail=956 Third Ridge))
Company(name=Enigma Cafe, address=Address(city=Mcdonough, detail=1278 Midway Trail))
Company(name=Hapeville Studios, address=Address(city=Riceboro, detail=823 Tuscarawas Blvd))
Company(name=Thalman Gymnasium, address=Address(city=Ebenezer, detail=1225 Blackwood Avenue))
Company(name=Sparks Pro Services, address=Address(city=Darien, detail=1362 Woodlawn Lane))
Company(name=Toccoa Development, address=Address(city=Ridgeville, detail=1790 Lawn Ave))
复制代码
有时候咱们对导入的数据有必定的要求,Octopus提供简单的数据校验配置
首先给咱们的Company
增长一个status
属性,只能是 good,bad和closed 三个值其中一个,同时name
不能够为空,看一下XML配置文件
<?xml version="1.0" encoding="UTF-8"?>
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://raw.githubusercontent.com/zerouwar/my-maven-repo/master/cn/chenhuanming/octopus/1.1.0/octopus.xsd" class="cn.chenhuanming.octopus.entity.Company">
<Field name="name" description="Name" color="#ff0000" is-blankable="false"/>
<Field name="address" description="Address" formatter="cn.chenhuanming.octopus.formatter.AddressFormatter" />
<Field name="status" description="Status" options="good|bad|closed"/>
<!--| split options -->
</Root>
复制代码
这是咱们要导入的Excel,能够看到里面有非法数据
看一下怎么编写Java代码
@Test
public void importCheckedData() throws IOException, InvalidFormatException {
InputStream is = this.getClass().getClassLoader().getResourceAsStream("wrongCompany.xlsx");
ConfigFactory configFactory = new XmlConfigFactory(this.getClass().getClassLoader().getResourceAsStream("company3.xml"));
final SheetReader<CheckedData<Company>> sheetReader = Octopus.readFirstSheetWithValidation(is,configFactory,new DefaultCellPosition(1,0));
for (CheckedData<Company> checkedData : sheetReader) {
System.out.println(checkedData);
}
}
复制代码
这里咱们调用Octopus.readFirstSheetWithValidation
,获取带校验结果的SheetReader
,看一下导入的结果
CheckedData(data=Company(name=Graham Motor Services, address=Address(city=Monroe, detail=666 Bonnair Ave), status=good), exceptions=[])
CheckedData(data=Company(name=Social Circle Engineering, address=Address(city=Fort Gaines, detail=956 Third Ridge), status=null), exceptions=[cn.chenhuanming.octopus.exception.NotAllowValueException])
CheckedData(data=Company(name=null, address=Address(city=Mcdonough, detail=1278 Midway Trail), status=null), exceptions=[cn.chenhuanming.octopus.exception.CanNotBeBlankException, cn.chenhuanming.octopus.exception.NotAllowValueException])
复制代码
能够看到每个CheckData
有一个data
属性和一个exceptions
列表。 这个异常列表存放着导入时每个单元格可能出现的校验错误,异常类型都是ParseException
除了is-blankable
和options
,还能够经过regex
配置正则表达式检查。当校验错误时,会抛出对应的ParseException
子类
is-blankable
:抛出 CanNotBeBlankException
options
:抛出 NotAllowValueException
regex
:抛出 PatternNotMatchException
你经过这些异常来进行跟进一步的处理。若是上面三种校验方式不能知足需求,在Formatter
的parse
抛出自定义的ParseException
。Octopus会捕获它们放到exceptions
列表中,并自动把单元格位置和你的配置内容塞到ParseException
中
以上代码均可以在测试路径cn.chenhuanming.octopus.example
找到,经过这些例子能够感觉下Octopus的魅力
目前只提供XML配置,由于XML和类文件解耦,有时候你没法修改类代码时,尤为是导出场景,XML会是更好的选择。若是你是"anti-xml",能够实现注解版ConfigFactory
,把注解配置转换成Field
,这应该不会很难。后面我有空再弄注解配置吧~
Octopus
类能够提供一行代码式的API,让你不用碰Apache POI的API。可是若是你确实须要用到Apache POI,能够先看一下Octopus核心类SheetWriter
和SheetReader
代码。我在设计的时候尽可能考虑扩展,而且彻底基于接口实现,实在不行能够选择继承重写,属性基本都是protected,或者直接本身实现接口
提Issue或者email我chenhuanming.cn@gmail.com