- 依赖
- 注解用法说明
- @ExcelTarget 绑定Id
- @Excel 用在字段上面
- @ExcelCollection
- 实体代码
- Excel导出
- 正常数量导出(1-2W条)
- 大数据量导出
- 多sheet导出
- 单元格超链接
- Excel导入
- importParams参数
- 正常导入 importExcel
- 大数据量 importExcelBySax
- 异步
- 读取合并的表头
- 验证导入(筛出符合与不符合的数据)
- 表头的groupName
- 导入图片
- CSV导入
- excel转换csv
- Excel转html
注解用法说明cn.afterturn easypoi-spring-boot-starter 4.3.0
- @Excel 用在字段上面
- @ExcelCollection 表示根对象中的list
- @ExcelEntity 表示根对象中的实体
- @ExcelIgnore 忽略导出
- @ExcelTarget 绑定Id
解决了相同的实体去映射Excel 但是表头名字不太相同 实现根据班级不同映射不同名字
这里的@ExcelTarget 表示使用X这个对象是可以针对不同字段做不同处理
同样的ExcelEntity 和ExcelCollection 都支持这种方式
| 属性 | 类型 | 默认值 | 功能 |
|---|---|---|---|
| name | String | null | 列名,支持ExcelTarget |
| needMerge | boolean | fasle | 纵向合并单元格(用于含有list中,单个的单元格,合并list创建的多个row) |
| orderNum | String | “0” | 列的排序,支持ExcelTarget |
| replace | String[] | {} | 字符替换 导出是{“替换值_源”,“替换值_源”} |
| savePath | String | “upload” | 导入文件保存路径,如果是图片可以填写,默认是upload/className/ IconEntity这个类对应的就是upload/Icon/ |
| type | int | 1 | 导出类型 1 是文本 2 是图片,3 是函数,10 是数字 默认是文本 |
| width | double | 10 | 列宽 |
| height | double | 10 | 列高,后期打算统一使用@ExcelTarget的height,这个会被废弃,注意 |
| isStatistics | boolean | fasle | 自动统计数据,在追加一行统计,把所有数据都和输出[这个处理会吞没异常,请注意这一点] |
| isHyperlink | boolean | FALSE | 超链接,如果是需要实现接口返回对象 |
| isimportField | boolean | TRUE | 校验字段,看看这个字段是不是导入的Excel中有,如果没有说明是错误的Excel,读取失败,支持ExcelTarget |
| exportFormat | String | “” | 导出的时间格式,以这个是否为空来判断是否需要格式化日期 |
| importFormat | String | “” | 导入的时间格式,以这个是否为空来判断是否需要格式化日期 |
| format | String | “” | 时间格式,相当于同时设置了exportFormat 和 importFormat |
| databaseFormat | String | “yyyyMMddHHmmss” | 导出时间设置,如果字段是Date类型则不需要设置 数据库如果是string 类型,这个需要设置这个数据库格式,用以转换时间格式输出 |
| numFormat | String | “” | 数字格式化,参数是Pattern,使用的对象是DecimalFormat |
| imageType | int | 1 | 导出类型 1 从file读取 2 是从数据库中读取 默认是文件 同样导入也是一样的 |
| suffix | String | “” | 文字后缀,如值实90 suffix="%" 变成90% |
| isWrap | boolean | TRUE | 是否换行 即支持n |
| mergeRely | int[] | {} | 合并单元格依赖关系,比如第二列合并是基于第一列 则{0}就可以了 |
| mergeVertical | boolean | fasle | 纵向合并内容相同的单元格 |
| fixedIndex | int | -1 | 对应excel的列,忽略名字 |
| isColumnHidden | boolean | FALSE | 导出隐藏列 |
看下如何使用 在下面有完全代码可以copy
@Data
@ExcelTarget("user1")
@Accessors(fluent = true)
public class TestBean implements Serializable {
@Excel(name = "A班级编号_user1,B班级编号_user2" ,//表头名称
width = 15.0, //宽度
orderNum = "0", //排序 这个排序在@ExcelEntity中也可以连贯使用 ExcelCollection内的Excel不可以连贯使用
needMerge = true)
private Integer id;
@Excel(name = "姓名",width = 15.0,orderNum = "1",needMerge = true)
private String name;
@Excel(name = "性別",
replace = {"男_1","女_2"}, //字符替换 源实1代表男 2代表女
suffix = "生",//添加后缀
orderNum = "2",needMerge = true)
private int sex;
@Excel(name = "生日", databaseFormat = "yyyyMMddHHmmss",//如果是字符串的话>>同步数据库格式
format = "yyyy-MM-dd HH:mm:ss",//设置导入导出格式
orderNum = "3",
needMerge = true)
private Date bir;
@ExcelIgnore //不生成数据
@Excel(name = "生日2", databaseFormat = "yyyy-MM-dd HH:mm:ss",format = "yyyy-MM-dd HH:mm:ss",orderNum = "4")
private Date bir2;
@Excel(name = "照片",type = 2,//设置数据类型 2是图片
imageType = 1,//图片使用方式
orderNum = "8",width = 30,height = 30)
private String img;
@Excel(name = "照片2",type = 2,imageType = 2,orderNum = "8",width = 30,height = 30)
private byte[] img1;
@ExcelEntity //表示这是一个实体 其实相当于在根实体中添加其他字段的感觉
private Card card;
@ExcelCollection(name = "物品",orderNum = "7")//这个集合会在这个对象内循环遍历..之前的字段数据只在第一行显示
private List orders;
@ExcelCollection
| 属性 | 类型 | 默认值 | 功能 |
|---|---|---|---|
| id | String | null | 定义ID |
| name | String | null | 定义集合列名,支持ExcelTarget |
| orderNum | int | 0 | 排序,支持ExcelTarget |
| type | Class> | ArrayList.class | 导入时创建对象使用 |
3个实体
@Data
@ExcelTarget("user1")
@Accessors(fluent = true)
public class TestBean implements Serializable {
public static String imgPath="imgs/company/baidu.png";
@Excel(name = "A班级编号_user1,B班级编号_user2" ,//表头名称
width = 15.0, //宽度
orderNum = "0", //排序 这个排序在@ExcelEntity中也可以连贯使用 ExcelCollection内的Excel不可以连贯使用
needMerge = true)
private Integer id;
@Excel(name = "姓名",width = 15.0,orderNum = "1",needMerge = true)
private String name;
@Excel(name = "性別",
replace = {"男_1","女_2"}, //字符替换 源实1代表男 2代表女
suffix = "生",//添加后缀
orderNum = "2",needMerge = true)
private int sex;
@Excel(name = "生日", databaseFormat = "yyyyMMddHHmmss",//如果是字符串的话>>同步数据库格式
format = "yyyy-MM-dd HH:mm:ss",//设置导入导出格式
orderNum = "3",
needMerge = true)
private Date bir;
@ExcelIgnore //不生成数据
@Excel(name = "生日2", databaseFormat = "yyyy-MM-dd HH:mm:ss",format = "yyyy-MM-dd HH:mm:ss",orderNum = "4")
private Date bir2;
@Excel(name = "照片",type = 2,//设置数据类型 2是图片
imageType = 1,//图片使用方式
orderNum = "8",width = 30,height = 30)
private String img;
@Excel(name = "照片2",type = 2,imageType = 2,orderNum = "8",width = 30,height = 30)
private byte[] img1;
@ExcelEntity //表示这是一个实体 其实相当于在根实体中添加其他字段的感觉
private Card card;
@ExcelCollection(name = "物品",orderNum = "7")//这个集合会在这个对象内循环遍历..之前的字段数据只在第一行显示
private List orders;
//获取10条 完整数据
public static List getListUser(){
List list=new ArrayList<>();
for (int i = 1; i <= 10; i++) {
list.add(new TestBean().id(i).name("姓名"+i).bir(new Date()).sex(i%2==0?1:2).card(new Card())
.orders(getOrder())
.img(imgPath));
}
return list;
}
//只获取根对象数据 没有图片,list,实体
public static List getBigListFor10000(int page){
List list=new ArrayList<>();
for (int i = page; i <= page*10000; i++) {
list.add(new TestBean().id(i).name("姓名"+i).bir(new Date()).sex(i%2==0?1:2));
}
return list;
}
public static List getOrder(){
List list=new ArrayList<>();
for (int i = 1; i <=3 ; i++) {
list.add(new Order().id(i).name("物品"+i));
}
return list;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public Date getBir() {
return bir;
}
public void setBir(Date bir) {
this.bir = bir;
}
public Date getBir2() {
return bir2;
}
public void setBir2(Date bir2) {
this.bir2 = bir2;
}
public String getImg() {
return img;
}
public void setImg(String img) {
this.img = img;
}
public byte[] getImg1() {
return img1;
}
public void setImg1(byte[] img1) {
this.img1 = img1;
}
public Card getCard() {
return card;
}
public void setCard(Card card) {
this.card = card;
}
public List getOrders() {
return orders;
}
public void setOrders(List orders) {
this.orders = orders;
}
//校验文件目录
public static String existsDir(){
String dir="D:/eazyPoi/";
File savefile = new File(dir);
if (!savefile.exists()) {
savefile.mkdirs();
}
return dir;
}
}
@Accessors(fluent = true)
@ExcelTarget("card1")
@Data
public class Card{
@Excel(name = "身份证",orderNum = "5")
private String id ;
@Excel(name = "地址",orderNum = "6")
private String addr;
public Card() {
this.id = "123212321";this.addr = "北京市朝阳区";
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
}
@Accessors(fluent = true)
@ExcelTarget("order1")
@Data
public class Order{
@Excel(name = "序号",orderNum = "1")
public int id ;
@Excel(name = "物品",orderNum = "2")
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Excel导出
正常数量导出(1-2W条)
package com.ruoyi.web.eazyPoi;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
import cn.afterturn.easypoi.excel.entity.ExportParams;
import cn.afterturn.easypoi.handler.inter.IExcelExportServer;
import cn.afterturn.easypoi.handler.inter.IWriter;
import org.apache.poi.hssf.record.DVALRecord;
import org.apache.poi.ss.usermodel.Workbook;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class Test {
public static void main(String[] args) throws Exception {
Test.exportExcel();
}
//1万条以下的方法
public static void exportExcel()throws Exception{
ExportParams exportParams=new ExportParams("用戶信息表", "用戶信息");
List listUser = TestBean.getListUser();
Workbook sheets = ExcelExportUtil.exportExcel(exportParams, TestBean.class, listUser);
sheets.write(new FileOutputStream(TestBean.existsDir()+System.currentTimeMillis() +".xls"));
sheets.close();
}
}
大数据量导出
这里面的这个@Override是一个循环函数,obj是外层传的queryParams(参4)但是page会一直循环到>obj 比比如我们分10页的数据 page会循环超过10
- sql条件分页到最后一页后查询为
public class Test {
public static void main(String[] args) throws Exception {
Test.exportExcel();
}
public static void exportBigExcel()throws Exception{
Workbook workbook = null;
Date start = new Date();
ExportParams exportParams=new ExportParams("用戶信息表", "用戶信息");
workbook = ExcelExportUtil.exportBigExcel(exportParams, TestBean.class, new IExcelExportServer() {
@Override
public List
多sheet导出
public void moveSheet(){
//sheet集合信息
List
单元格超链接
@Data
public class HyperlinkEntity {
@Excel(name = "名称", isHyperlink = true)
private String name;
@Excel(name = "URL")
private String url;
@Excel(name="备注")
private String comment;
}
@Test
public void test() throws Exception {
//数据集合
List list = new ArrayList();
HyperlinkEntity client = new HyperlinkEntity();
client.setName("百度");
client.setUrl("https://www.baidu.com/");
client.setComment("XXXXXXXXXX");
list.add(client);
client = new HyperlinkEntity();
client.setName("新浪");
client.setUrl("http://www.sina.com.cn/");
client.setComment("XXXXXXXXXX");
list.add(client);
//表格信息
ExportParams params = new ExportParams("超链接测试", "超链接测试", ExcelType.XSSF);
//设置拦截
params.setDataHandler(new ExcelDataHandlerDefaultImpl() {
@Override
public Hyperlink getHyperlink(CreationHelper creationHelper, Object obj, String name, Object value) {
Hyperlink link = creationHelper.createHyperlink(HyperlinkType.URL);
System.out.println(name);
System.out.println(value);
HyperlinkEntity e = (HyperlinkEntity) obj;
link.setAddress(e.getUrl());//超链接地址
link.setLabel(e.getName());//返回此超链接的文本标签
HyperlinkType type = link.getType();//获取超链接类型HyperlinkType枚举
return link;
}
});
Workbook workbook = ExcelExportUtil.exportExcel(params, HyperlinkEntity.class, list);
FileOutputStream fos = new FileOutputStream("D:/home/excel/ExcelExportForlink.xlsx");
workbook.write(fos);
fos.close();
}
Excel导入
importParams参数
| 值 | 类型 | 默认值 | 功能 |
|---|---|---|---|
| titleRows | int | 0 | 表格标题行数,默认0 |
| headRows | int | 1 | 表头行数,默认1 |
| startRows | int | 0 | 设置开始读取的位置,也就是从标题下0为全部 1读取第一行以后的数据 |
| keyIndex | int | 0 | 主键设置,如何这个cell没有值,就跳过 或者认为这个是list的下面的值,这一列必须有值,不然认为这列为无效数据 |
| startSheetIndex | int | 0 | 与sheetNum两个配合使用 在多个sheet时 有10个sheet我们想读取1-4个sheet |
| sheetNum | int | 1 | 上传表格需要读取的sheet 数量,默认为1 |
| needSave | boolean | FALSE | 是否需要保存上传的Excel |
| needVerfiy | boolean | FALSE | 是否需要校验上传的Excel |
| saveUrl | String | “upload/excelUpload” | 保存上传的Excel目录,默认是 如 TestEntity这个类保存路径就是upload/excelUpload/Test/yyyyMMddHHmss_ 保存名称上传时间_五位随机数 |
| verifyHanlder | IExcelVerifyHandler | null | 校验处理接口,自定义校验 |
| lastOfInvalidRow | int | 0 | 最后的无效行数,不读的行数 |
| readRows | int | 0 | 手动控制读取的行数实测设置head后 2=第一行数据 |
| importFields | String[] | null | excel文件中必须要有的字段,必须要有这些数据 |
| keyMark | String | “:” | Key-Value 读取标记,以这个为Key,后面一个Cell 为Value,多个改为ArrayList |
| readSingleCell | boolean | FALSE | 按照Key-Value 规则读取全局扫描Excel,但是跳过List读取范围提升性能,仅仅支持titleRows + headRows + startRows 以及 lastOfInvalidRow |
| dataHanlder | IExcelDataHandler | null | 数据处理接口,以此为主,replace,format都在这后面 |
需要把实体里面图片的字段的Excel注释掉
//普通正常表格
public void importExcel1() {
importParams params = new importParams();
params.setTitleRows(1);
params.setHeadRows(1);
params.setStartRows(0);//设置开始读取的位置,也就是从标题下0为全部 1读取第一行以后的数据
// params.setKeyIndex(1);//
// params.setStartSheetIndex(0); params.setSheetNum(1);//两个配合使用 在多个sheet时 有10个sheet我们想读取1-4个sheet
// params.setReadRows(3);//实测设置head后 2=第一行数据
params.setimportFields(new String[]{"姓名","性別"});//excel文件中必须要有的字段,必须要有这些数据
// params.setReadRows(4);
File file = new File(getWebRootPath("import/B.xlsx"));
List list = ExcelimportUtil.importExcel(file,TestBean.class, params);
list.stream().forEach(i-> System.out.println(i.toString()));
}
大数据量 importExcelBySax
//导入大量数据
public void importExcelBySax() throws FileNotFoundException {
importParams params = new importParams();
params.setTitleRows(1);
List list=new ArrayList<>();
ExcelimportUtil.importExcelBySax(
new FileInputStream(
new File(getWebRootPath("import/ExcelExportMsgClient.xlsx"))),
TestBean.class, params, new IReadHandler() {
@Override//读取的每一行
public void handler(TestBean o) {
list.add(o);
System.out.println(ReflectionToStringBuilder.toString(o));
}
@Override
public void doAfterAll() {
System.out.println("数据执行完毕的处理");
}
});
}
异步
从官方的demo中看得出 没快哪里去
public void asyncTask(){
importParams params =new importParams();
params.setHeadRows(1);
params.setTitleRows(1);
params.setConcurrentTask(true);//开启异步
params.setCritical(500);
List objects = ExcelimportUtil.importExcel(
new File(getWebRootPath("import/B.xlsx")),
TestBean.class,
params
);
objects.stream().forEach(i-> System.out.println(i.toString()));
}
读取合并的表头
//普通正常表格
public void importExcel1() {
importParams params = new importParams();
params.setTitleRows(1);//标题1行
params.setHeadRows(2);//表头一共是2行
File file = new File(getWebRootPath("import/B.xlsx"));
List list = ExcelimportUtil.importExcel(file,TestBean.class, params);
list.stream().forEach(i-> System.out.println(i.toString()));
}
验证导入(筛出符合与不符合的数据)
官方给出挺多的demo,有点繁琐,觉得这个是比较实用的
在实体类中添加hibernate的字段验证注解
这个不需要额外引入依赖
主要验证这3个位置
public void validFieldimport() throws IOException {
importParams params=new importParams();
params.setTitleRows(1);
params.setHeadRows(2);
params.setNeedVerify(true);//设置需要验证
params.setVerifyGroup(new Class[]{ValidGroup.class});//设置验证分组
ExcelimportResult importResult = ExcelimportUtil.importExcelMore(new File(getWebRootPath("import/B.xlsx")), TestBean.class, params);
//符合验证
List list = importResult.getList();
//不符合验证
List failList = importResult.getFailList();
//获取map
Map map = importResult.getMap();
//导出验证的数据
importResult.getWorkbook().write(new FileOutputStream("D:/eazyPoi/validExcel.xlsx"));
//不符合的数据
importResult.getFailWorkbook().write(new FileOutputStream("D:/eazyPoi/failValidExcel.xlsx"));
System.out.println("是否验证失败"+importResult.isVerifyFail());
}
bir 字段没有加验证分组 所以不参与验证
表头的groupName失败导出的excel会有提示
@Excel(name = "公司LOGO", type = 2 ,width = 40 , height = 30,imageType = 1,
savePath="D:\javaProject\")
private String companyLogo;
CSV导入
新建C.csv文件
id,name,sex,bir 1,小明,1,1991-08-08 2,小张,1,1991-09-08 3,小红,2,1991-09-08
public void importCSV()throws Exception{
Date start = new Date();
CsvimportParams params = new CsvimportParams(CsvimportParams.UTF8);
CsvimportUtil.importCsv(new FileInputStream(
new File(getWebRootPath("import/C.csv"))),
Map.class, params, new IReadHandler() {
@Override
public void handler(Map o) {
System.out.println(JSON.toJSONString(o));
}
@Override
public void doAfterAll() {
}
});
}
excel转换csv
Excel
public void excel2Csv()throws Exception{
//输出的CSV
FileOutputStream fos = new FileOutputStream("D:\eazyPoi\aa.csv");
importParams params = new importParams();
params.setTitleRows(1);
params.setHeadRows(1);
CsvExportParams csvExportParams = new CsvExportParams();
csvExportParams.setEncoding(CsvExportParams.GBK);//可使用UTF8
IWriter ce = CsvExportUtil.exportCsv(csvExportParams, TestBean.class, fos);
ExcelimportUtil.importExcelBySax(
new FileInputStream(
new File(getWebRootPath("import/B.xlsx"))),
TestBean.class, params, new IReadHandler() {
private List list = new ArrayList<>();
@Override
public void handler(TestBean o) {
//读取后添加到list
list.add(o);
}
@Override
public void doAfterAll() {
//读取结束后转换输出
ce.write(list);
list.clear();
System.out.println("succcess--------------------------------");
}
});
}
Excel转htmlCSV 红框为实体类注解字段
public void testToAllHtmlWorkbookAndImage()throws Exception{
Workbook wb = new XSSFWorkbook(new FileInputStream(new File(getWebRootPath("import/B.xlsx"))));
String html = ExcelXorHtmlUtil.excelToHtml(new ExcelToHtmlParams(wb, true, "D:\eazyPoi\"));
FileWriter fw = new FileWriter("D:/eazyPoi/B.html");
fw.write(html);
fw.close();
}



