Excel作为常用的报表文件,项目开发中经常用到Excel操作功能,对于Excel操作目前常见的有三种方式,Apache的POI,Hutool的ExcelUtil,Ali的EasyExcel。
Apache的POI相对比较原生,功能强大,使用复杂繁琐,内存占用比较大。
Hutool的ExcelUtil在POI基础上进行封装,简单易用,功能不太全,对于一些特殊操作,如标注等不具备。
Ali的EasyExcel也是在POI基础上进行封装,简单易用,功能相对比较全面,且性能十分强悍,最为推荐。
Apache POI [1] 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office格式档案读和写的功能。POI为“Poor Obfuscation Implementation”的首字母缩写,意为“简洁版的模糊实现”。
HSSF [1] - 提供读写Microsoft Excel XLS格式档案的功能。
XSSF [1] - 提供读写Microsoft Excel OOXML XLSX格式档案的功能。
HWPF [1] - 提供读写Microsoft Word DOC格式档案的功能。
HSLF [1] - 提供读写Microsoft PowerPoint格式档案的功能。
HDGF [1] - 提供读Microsoft Visio格式档案的功能。
HPBF [1] - 提供读Microsoft Publisher格式档案的功能。
HSMF [1] - 提供读Microsoft Outlook格式档案的功能。
POI官网地址:https://poi.apache.org/index.html
POI下载地址:https://poi.apache.org/download.html
POI文档说明:https://poi.apache.org/apidocs/index.html
Maven依赖配置
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poiartifactId>
<version>4.0.1version>
dependency>
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poi-ooxmlartifactId>
<version>4.0.1version>
dependency>
<dependency>
<groupId>org.apache.poigroupId>
<artifactId>poi-ooxml-schemasartifactId>
<version>4.0.1version>
dependency>
代码测试
package com.zrj.easyexcel.excel;
import com.google.common.collect.Lists;
import org.junit.Test;
import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 原生Excel测试类
*
* @author zrj
* @since 2021/12/29
**/
public class EasyExcelPoi {
@Test
public void createExcelTest() throws Exception {
//文件的基础路径
String fileBasePath = System.getProperty("user.dir") + File.separator + "excel" + File.separator;
System.out.println("文件的基础路径:" + fileBasePath);
String filepath = fileBasePath + "writeexcel.xlsx";
String sheetName = "模板名称";
List<String> titles = Lists.newArrayList("name", "sex", "age");
List<Map<String, Object>> values = Lists.newArrayList();
Map map2 = new HashMap(6);
map2.put("name", "王五");
map2.put("sex", "女");
map2.put("age", "20");
values.add(map2);
Map map3 = new HashMap(6);
map3.put("name", "阿萨");
map3.put("sex", "女");
map3.put("age", "66");
values.add(map3);
ExcelUtils.writeExcel(filepath, sheetName, titles, values);
System.out.println("创建Excel完成");
}
@Test
public void copyExcelTest() throws Exception {
//文件的基础路径
String fileBasePath = System.getProperty("user.dir") + File.separator + "excel" + File.separator;
System.out.println("文件的基础路径:" + fileBasePath);
String filepath = fileBasePath + "writeexcel.xlsx";
String fileNewPath = fileBasePath + "writeexcel2.xlsx";
ExcelUtils.writeExcel(filepath, fileNewPath);
System.out.println("复制Excel完成");
}
@Test
public void readExcelTest() throws Exception {
//文件的基础路径
String fileBasePath = System.getProperty("user.dir") + File.separator + "excel" + File.separator;
String filepath = fileBasePath + "writeexcel.xlsx";
System.out.println("文件路径:" + fileBasePath);
String readExcel = ExcelUtils.readExcel(filepath);
System.out.println(readExcel);
System.out.println("读取Excel完成");
}
}
Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。
Hutool中的工具方法来自每个用户的精雕细琢,它涵盖了Java开发底层代码中的方方面面,它既是大型项目开发中解决小问题的利器,也是小型项目中的效率担当;
Hutool是项目中“util”包友好的替代,它节省了开发人员对项目中公用类和公用工具方法的封装时间,使开发专注于业务,同时可以最大限度的避免封装不完善带来的bug。
项目中使用非常广泛的一个小工具,没有依赖,非常干净好用。
官网地址:https://www.hutool.cn/
参考文档:https://www.hutool.cn/docs/#/
API文档:https://apidoc.gitee.com/dromara/hutool/
GITEE:https://gitee.com/dromara/hutool/
GITHUB:https://github.com/dromara/hutool/
视频介绍:https://player.bilibili.com/player.html?aid=710062843&bvid=BV1bQ4y1M7d9&cid=170377135&page=2
Mave依赖配置
<dependency>
<groupId>cn.hutoolgroupId>
<artifactId>hutool-allartifactId>
<version>5.7.18version>
dependency>
代码实现验证
package com.zrj.easyexcel.excel;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.zrj.easyexcel.utils.FileUtils;
import org.junit.Test;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* HutoolExcel测试类
* 注意:不要到错包了,easyexcel与hutool的类名一样!!!
*
* @author zrj
* @since 2021/12/29
**/
public class EasyExcelHutool {
/*******************************Excel读取-ExcelReader*******************************************/
/**
* 1. 读取Excel中所有行和列,都用列表表示
*/
@Test
public void readExcelObjectTest() {
//定义文件路径
String filepath = System.getProperty("user.dir") + File.separator + "excel" + File.separator + "writeBeanKeyTest.xlsx";
//通过工具类创建ExcelReader
ExcelReader reader = ExcelUtil.getReader(filepath);
List<List<Object>> readAll = reader.read();
System.out.println("Excel读取结果:" + readAll);
}
/**
* 2. 读取为Map列表,默认第一行为标题行,Map中的key为标题,value为标题对应的单元格值。
*/
@Test
public void readExcelMapTest() {
//定义文件路径
String filepath = System.getProperty("user.dir") + File.separator + "excel" + File.separator + "writeBeanKeyTest.xlsx";
//通过工具类创建ExcelReader
ExcelReader reader = ExcelUtil.getReader(filepath);
List<Map<String, Object>> readAll = reader.readAll();
System.out.println("Excel读取结果:" + readAll);
}
/**
* 3. 读取为Bean列表,Bean中的字段名为标题,字段值为标题对应的单元格值。
*/
@Test
public void readExcelBeanTest() {
//定义文件路径
String filepath = System.getProperty("user.dir") + File.separator + "excel" + File.separator + "writeBeanKeyTest.xlsx";
//通过工具类创建ExcelReader
ExcelReader reader = ExcelUtil.getReader(filepath);
List<UserBean> readAll = reader.readAll(UserBean.class);
System.out.println("Excel读取结果:" + readAll);
}
/*******************************Excel生成-ExcelWriter*******************************************/
/**
* 1. 将行列对象写出到Excel
*/
@Test
public void writeExcelObjectTest() {
//定义一个嵌套的List,List的元素也是一个List,内层的一个List代表一行数据,每行都有4个单元格,最终list对象代表多行数据。
List<String> row1 = CollUtil.newArrayList("aa", "bb", "cc", "dd");
List<String> row2 = CollUtil.newArrayList("aa1", "bb1", "cc1", "dd1");
List<String> row3 = CollUtil.newArrayList("aa2", "bb2", "cc2", "dd2");
List<String> row4 = CollUtil.newArrayList("aa3", "bb3", "cc3", "dd3");
List<String> row5 = CollUtil.newArrayList("aa4", "bb4", "cc4", "dd4");
List<List<String>> rows = CollUtil.newArrayList(row1, row2, row3, row4, row5);
//定义文件路径
String filepath = System.getProperty("user.dir") + File.separator + "excel" + File.separator + "writeTest.xlsx";
//通过工具类创建writer
ExcelWriter writer = ExcelUtil.getWriter(filepath);
//通过构造方法创建writer
//ExcelWriter writer = new ExcelWriter("d:/writeTest.xls");
//跳过当前行,既第一行,非必须,在此演示用
writer.passCurrentRow();
//合并单元格后的标题行,使用默认标题样式
writer.merge(row1.size() - 1, "测试标题");
//一次性写出内容,强制输出标题
writer.write(rows, true);
//关闭writer,释放内存
writer.close();
System.out.println("创建完成,文件路径:" + filepath);
}
/**
* 2. 写出Map数据
*/
@Test
public void writeExcelMapTest() {
Map<String, Object> row1 = new LinkedHashMap<>();
row1.put("姓名", "张三");
row1.put("年龄", 23);
row1.put("成绩", 88.32);
row1.put("是否合格", true);
row1.put("考试日期", DateUtil.date());
Map<String, Object> row2 = new LinkedHashMap<>();
row2.put("姓名", "李四");
row2.put("年龄", 33);
row2.put("成绩", 59.50);
row2.put("是否合格", false);
row2.put("考试日期", DateUtil.date());
ArrayList<Map<String, Object>> rows = CollUtil.newArrayList(row1, row2);
//定义文件路径
String filepath = System.getProperty("user.dir") + File.separator + "excel" + File.separator + "writeMapTest.xlsx";
// 通过工具类创建writer
ExcelWriter writer = ExcelUtil.getWriter(filepath);
// 合并单元格后的标题行,使用默认标题样式
writer.merge(row1.size() - 1, "一班成绩单");
// 一次性写出内容,使用默认样式,强制输出标题
writer.write(rows, true);
// 关闭writer,释放内存
writer.close();
System.out.println("创建完成,文件路径:" + filepath);
}
/**
* 3. 写出Bean数据
*/
@Test
public void writeExcelBeanTest() {
UserBean bean1 = UserBean.builder().name("张三").age(22).isPass(true).score(66.30).examDate(DateUtil.date()).build();
UserBean bean2 = UserBean.builder().name("李四").age(30).isPass(false).score(38.50).examDate(DateUtil.date()).build();
List<UserBean> rows = CollUtil.newArrayList(bean1, bean2);
//定义文件路径
String filepath = System.getProperty("user.dir") + File.separator + "excel" + File.separator + "writeBeanTest.xlsx";
// 通过工具类创建writer
ExcelWriter writer = ExcelUtil.getWriter(filepath);
// 合并单元格后的标题行,使用默认标题样式
writer.merge(4, "一班成绩单");
// 一次性写出内容,使用默认样式,强制输出标题
writer.write(rows, true);
// 关闭writer,释放内存
writer.close();
System.out.println("创建完成,文件路径:" + filepath);
}
/**
* 4. 自定义Bean的key别名(排序标题)
* 注意:与3的区别是,3中标题是英文,4中标题是自定义的别名
*/
@Test
public void writeExcelBeanKeyTest() {
UserBean bean1 = UserBean.builder().name("张三").age(22).isPass(true).score(66.30).examDate(DateUtil.date()).build();
UserBean bean2 = UserBean.builder().name("李四").age(30).isPass(false).score(38.50).examDate(DateUtil.date()).build();
List<UserBean> rows = CollUtil.newArrayList(bean1, bean2);
//定义文件路径
String filepath = System.getProperty("user.dir") + File.separator + "excel" + File.separator + "writeBeanKeyTest.xlsx";
// 通过工具类创建writer
ExcelWriter writer = ExcelUtil.getWriter(filepath);
//自定义标题别名
writer.addHeaderAlias("name", "姓名");
writer.addHeaderAlias("age", "年龄");
writer.addHeaderAlias("score", "分数");
writer.addHeaderAlias("isPass", "是否通过");
writer.addHeaderAlias("examDate", "考试时间");
// 默认的,未添加alias的属性也会写出,如果想只写出加了别名的字段,可以调用此方法排除之
writer.setOnlyAlias(true);
// 合并单元格后的标题行,使用默认标题样式
writer.merge(4, "一班成绩单");
// 一次性写出内容,使用默认样式,强制输出标题
writer.write(rows, true);
// 关闭writer,释放内存
writer.close();
System.out.println("创建完成,文件路径:" + filepath);
}
/**
* 5. 写出到流
*/
@Test
public void writeExcelStreamTest() {
//定义内容
UserBean bean1 = UserBean.builder().name("张三").age(22).isPass(true).score(66.30).examDate(DateUtil.date()).build();
UserBean bean2 = UserBean.builder().name("李四").age(30).isPass(false).score(38.50).examDate(DateUtil.date()).build();
List<UserBean> rows = CollUtil.newArrayList(bean1, bean2);
//定义输出流
ByteArrayOutputStream out = FileUtils.cloneInputStream(IoUtil.toUtf8Stream(rows.toString()));
//定义文件路径
String filepath = System.getProperty("user.dir") + File.separator + "excel" + File.separator + "writeStreamTest.xlsx";
if (!FileUtil.isFile(filepath)) {
FileUtil.touch(filepath);
}
//通过工具类创建writer
ExcelWriter writer = ExcelUtil.getWriter();
//创建xlsx格式的
//ExcelWriter writer = ExcelUtil.getWriter(true);
//一次性写出内容,使用默认样式,强制输出标题
writer.write(rows, true);
//out为OutputStream,需要写出到的目标流
writer.flush(out);
//关闭writer,释放内存
writer.close();
System.out.println("创建完成,文件路径:" + filepath);
}
/**
* 6. 写出到客户端下载(写出到Servlet)
*/
@Test
public void writeExcelDownloadTest() {
//定义内容
UserBean bean1 = UserBean.builder().name("张三").age(22).isPass(true).score(66.30).examDate(DateUtil.date()).build();
UserBean bean2 = UserBean.builder().name("李四").age(30).isPass(false).score(38.50).examDate(DateUtil.date()).build();
List<UserBean> rows = CollUtil.newArrayList(bean1, bean2);
// 通过工具类创建writer,默认创建xls格式
ExcelWriter writer = ExcelUtil.getWriter();
// 一次性写出内容,使用默认样式,强制输出标题
writer.write(rows, true);
//out为OutputStream,需要写出到的目标流
//response为HttpServletResponse对象
//response.setContentType("application/vnd.ms-excel;charset=utf-8");
//test.xls是弹出下载对话框的文件名,不能为中文,中文请自行编码
//response.setHeader("Content-Disposition", "attachment;filename=test.xls");
//ServletOutputStream out = response.getOutputStream();
//writer.flush(out, true);
//关闭writer,释放内存
writer.close();
//此处记得关闭输出Servlet流
//IoUtil.close(out);
}
}
EasyExcel是阿里巴巴开源的一款Excel操作工具,简单易用,功能强大,节省内存,性能彪悍。
语雀地址:https://www.yuque.com/easyexcel/doc/easyexcel
Github地址: https://github.com/alibaba/easyexcel
mvn依赖地址:https://mvnrepository.com/artifact/com.alibaba/easyexcel
Mave依赖配置
<dependency>
<groupId>com.alibabagroupId>
<artifactId>easyexcelartifactId>
<version>3.0.5version>
dependency>
EasyExcel基本实现案例
Github案例地址:https://github.com/alibaba/easyexcel
项目路径:test -> read/write -> ReadTest与WriteTest类,实现基本Excel各种读写功能。
EasyExcel批量添加批注
ByteToInputStreamUtil
package com.zrj.easyexcel.excel;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
/**
* 输入流与字节转换
*
* @author zrj
* @since 2021/12/27
**/
public class ByteToInputStreamUtil {
/**
* 字节转输入流
*
* @param buf
* @return java.io.InputStream
*/
public static InputStream byte2Input(byte[] buf) {
return new ByteArrayInputStream(buf);
}
/**
* 输入流转字节
*
* @param inStream
* @return byte[]
*/
public static byte[] input2byte(InputStream inStream) throws Exception {
ByteArrayOutputStream swapStream = new ByteArrayOutputStream();
byte[] buff = new byte[100];
int rc = 0;
while ((rc = inStream.read(buff, 0, 100)) > 0) {
swapStream.write(buff, 0, rc);
}
byte[] in2b = swapStream.toByteArray();
return in2b;
}
}
CommentWriteHandler
package com.zrj.easyexcel.excel;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.write.handler.AbstractRowWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.poi.hssf.usermodel.HSSFClientAnchor;
import org.apache.poi.hssf.usermodel.HSSFRichTextString;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFRichTextString;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 批注处理器
*
* @author zrj
* @since 2021/12/29
**/
public class CommentWriteHandler extends AbstractRowWriteHandler {
/**
* sheet名称KEY
*/
public static final String SHEETNAME_NAME = "sheetName";
/**
* 文档后缀名
*/
private String extension;
/**
* 列索引key
*/
public static final String COLINDEX_NAME = "colIndex";
/**
* 行索引key
*/
public static final String ROWINDEX_NAME = "rowIndex";
/**
* 批注内容key
*/
public static final String COMMENTCONTENT_NAME = "commentContent";
/**
* sheet页名称列表
*/
private List<String> sheetNameList;
/**
* 批注集合
*/
List<Map<String, String>> commentList = new ArrayList<>(10);
/**
* CommentWriteHandler
*
* @param commentList
* @param extension
*/
public CommentWriteHandler(List<Map<String, String>> commentList, String extension) {
this.commentList = commentList != null && commentList.size() > 0
? commentList.stream().filter(x ->
x.keySet().contains(SHEETNAME_NAME) == true && x.get(SHEETNAME_NAME) != null && StrUtil.isNotBlank(x.get(SHEETNAME_NAME).toString())
&& x.keySet().contains(COLINDEX_NAME) == true && x.get(COLINDEX_NAME) != null && StrUtil.isNotBlank(x.get(COLINDEX_NAME).toString())
&& x.keySet().contains(ROWINDEX_NAME) == true && x.get(ROWINDEX_NAME) != null && StrUtil.isNotBlank(x.get(ROWINDEX_NAME).toString())
&& x.keySet().contains(COMMENTCONTENT_NAME) == true && x.get(COMMENTCONTENT_NAME) != null && StrUtil.isNotBlank(x.get(COMMENTCONTENT_NAME).toString())
).collect(Collectors.toList()) : new ArrayList<>();
sheetNameList = this.commentList.stream().map(x -> x.get(SHEETNAME_NAME).toString()).collect(Collectors.toList());
this.extension = extension;
}
/**
* 生成批注信息
*
* @param sheetName sheet页名称
* @param rowIndex 行号
* @param columnIndex 列号
* @param commentContent 批注内容
* @return
*/
public static Map<String, String> createCommentMap(String sheetName, int rowIndex, int columnIndex, String commentContent) {
Map<String, String> map = new HashMap<>();
//sheet页名称
map.put(SHEETNAME_NAME, sheetName);
//行号
map.put(ROWINDEX_NAME, rowIndex + "");
//列号
map.put(COLINDEX_NAME, columnIndex + "");
//批注内容
map.put(COMMENTCONTENT_NAME, commentContent);
return map;
}
/**
* 功能描述
* @param writeSheetHolder
* @param writeTableHolder
* @param row
* @param relativeRowIndex
* @param isHead
* @return void
*/
@Override
public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row,
Integer relativeRowIndex, Boolean isHead) {
Sheet sheet = writeSheetHolder.getSheet();
//不需要添加批注,或者当前sheet页不需要添加批注
if (commentList == null || commentList.size() <= 0 || sheetNameList.contains(sheet.getSheetName()) == false) {
return;
}
//获取当前行的批注信息
List<Map<String, String>> rowCommentList = commentList.stream().filter(x ->
StrUtil.equals(x.get(SHEETNAME_NAME).toString(), sheet.getSheetName())
&& relativeRowIndex == Integer.parseInt(x.get(ROWINDEX_NAME))).collect(Collectors.toList());
//当前行没有批注信息
if (rowCommentList == null || rowCommentList.size() <= 0) {
return;
}
List<String> colIndexList = rowCommentList.stream().map(x -> x.get(COLINDEX_NAME)).distinct().collect(Collectors.toList());
for (String colIndex : colIndexList) {
//同一单元格的批注信息
List<Map<String, String>> cellCommentList = rowCommentList.stream().filter(x ->
StrUtil.equals(colIndex, x.get(COLINDEX_NAME))).collect(Collectors.toList());
if (CollectionUtil.isEmpty(cellCommentList)) {
continue;
}
//批注内容拼成一条
String commentContent = cellCommentList.stream().map(x -> x.get(COMMENTCONTENT_NAME)).collect(Collectors.joining());
Cell cell = row.getCell(Integer.parseInt(colIndex));
addComment(cell, commentContent, extension);
}
//删除批注信息
commentList.remove(rowCommentList);
//重新获取要添加的sheet页姓名
sheetNameList = commentList.stream().map(x -> x.get(SHEETNAME_NAME).toString()).collect(Collectors.toList());
}
/**
* 给Cell添加批注
*
* @param cell 单元格
* @param value 批注内容
* @param extension 扩展名
*/
public static void addComment(Cell cell, String value, String extension) {
Sheet sheet = cell.getSheet();
cell.removeCellComment();
if ("xls".equals(extension)) {
ClientAnchor anchor = new HSSFClientAnchor();
// 关键修改
anchor.setDx1(0);
anchor.setDx2(0);
anchor.setDy1(0);
anchor.setDy2(0);
anchor.setCol1(cell.getColumnIndex());
anchor.setRow1(cell.getRowIndex());
anchor.setCol2(cell.getColumnIndex() + 5);
anchor.setRow2(cell.getRowIndex() + 6);
// 结束
Drawing drawing = sheet.createDrawingPatriarch();
Comment comment = drawing.createCellComment(anchor);
// 输入批注信息
comment.setString(new HSSFRichTextString(value));
// 将批注添加到单元格对象中
cell.setCellComment(comment);
} else if ("xlsx".equals(extension)) {
ClientAnchor anchor = new XSSFClientAnchor();
// 关键修改
anchor.setDx1(0);
anchor.setDx2(0);
anchor.setDy1(0);
anchor.setDy2(0);
anchor.setCol1(cell.getColumnIndex());
anchor.setRow1(cell.getRowIndex());
anchor.setCol2(cell.getColumnIndex() + 5);
anchor.setRow2(cell.getRowIndex() + 6);
// 结束
Drawing drawing = sheet.createDrawingPatriarch();
Comment comment = drawing.createCellComment(anchor);
// 输入批注信息
comment.setString(new XSSFRichTextString(value));
// 将批注添加到单元格对象中
cell.setCellComment(comment);
}
}
}
EasyExcelUtils
package com.zrj.easyexcel.excel;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.zrj.easyexcel.entity.DemoData;
import com.zrj.easyexcel.excel.read.ReadTest;
import com.zrj.easyexcel.excel.write.WriteTest;
import org.junit.Test;
import java.io.File;
import java.util.*;
/**
* EasyExcel测试类
*
* @author zrj
* @since 2021/12/29
**/
public class EasyExcelUtils {
/**
* EasyExcel读写测试类
*/
@Test
public void easyExcelReadWriteTest() {
//EasyExcel读测试类
ReadTest readTest = new ReadTest();
//EasyExcel写测试类
WriteTest writeTest = new WriteTest();
}
/**
* EasyExcel 批量添加批注
*/
@Test
public void batchAddCommentTest() {
try {
//构建数据
List<DemoData> demoDataList = new ArrayList<>(10);
demoDataList.add(DemoData.builder().string("张三").date(new Date()).doubleData(3.14).build());
demoDataList.add(DemoData.builder().string("王五").date(new Date()).doubleData(6.68).build());
demoDataList.add(DemoData.builder().string("赵六").date(new Date()).doubleData(8.32).build());
demoDataList.add(DemoData.builder().string("李四").date(new Date()).doubleData(8.66).build());
//定义文件路径
String filepath = System.getProperty("user.dir") + File.separator + "excel" + File.separator + "EasyExcelCommentWriteTest3.xlsx";
String sheetName = "批注模板";
List<Map<String, String>> commentList = new ArrayList<>();
commentList.add(CommentWriteHandler.createCommentMap(sheetName, 0, 1, "第一条批注。"));
commentList.add(CommentWriteHandler.createCommentMap(sheetName, 1, 1, "第二条批注。"));
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// 这里要注意inMemory 要设置为true,才能支持批注。目前没有好的办法解决 不在内存处理批注。这个需要自己选择。
EasyExcel.write(filepath, DemoData.class).inMemory(Boolean.TRUE)
.registerWriteHandler(new CommentWriteHandler(commentList, "xlsx"))
.sheet(sheetName).doWrite(demoDataList);
System.out.println("批注模板完成,模板地址:" + filepath);
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试源码地址:https://gitee.com/rjzhu/opencode/tree/master/easyexcel