EasyExcel
注意点
不支持的功能
- 单个文件的并发写入、读取
- 读取图片
- 宏
- csv读取
出现 NoSuchMethodException, ClassNotFoundException, NoClassDefFoundError
- jar包冲突
读Excel
easyExcel.xlsx文件
地区 | 2000年人口数(万人) | 2000年比重 |
---|---|---|
安徽省 | 5986 | 4.73% |
北京市 | 1382 | 1.09% |
福建省 | 3471 | 2.74% |
甘肃省 | 2562 | 2.02% |
广东省 | 8642 | 6.83% |
广西壮族自治区 | 4489 | 3.55% |
贵州省 | 3525 | 2.78% |
海南省 | 787 | 0.62% |
河北省 | 6744 | 5.33% |
河南省 | 9256 | 7.31% |
黑龙江省 | 3689 | 2.91% |
湖北省 | 6028 | 4.76% |
湖南省 | 6440 | 5.09% |
吉林省 | 2728 | 2.16% |
江苏省 | 7438 | 5.88% |
江西省 | 4140 | 3.27% |
辽宁省 | 4238 | 3.35% |
难以确定常住地 | 105 | 0.08% |
内蒙古自治区 | 2376 | 1.88% |
宁夏回族自治区 | 562 | 0.44% |
青海省 | 518 | 0.41% |
山东省 | 9079 | 7.17% |
山西省 | 3297 | 2.60% |
陕西省 | 3605 | 2.85% |
上海市 | 1674 | 1.32% |
四川省 | 8329 | 6.58% |
天津市 | 1001 | 0.79% |
西藏自治区 | 262 | 0.21% |
新疆维吾尔自治区 | 1925 | 1.52% |
云南省 | 4288 | 3.39% |
浙江省 | 4677 | 3.69% |
中国人民解放军现役军人 | 250 | 0.20% |
重庆市 | 3090 | 2.44% |
对象
public class MyExcel {
private String province;
private Integer personNum;
// 接收百分比的数字
@NumberFormat("#.##")
private String percent;
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public Integer getPersonNum() {
return personNum;
}
public void setPersonNum(Integer personNum) {
this.personNum = personNum;
}
public String getPercent() {
return percent;
}
public void setPercent(String percent) {
this.percent = percent;
}
}
简单的读取
- 创建excel的对象
- 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器
- 直接读即可
监听器类
/**
* 监听器
* 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
*/
public class MyExcelListener extends AnalysisEventListener<MyExcel> {
private static final Logger LOGGER = LoggerFactory.getLogger(MyExcelListener.class);
//控制存放在内存的数据的条数 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
private static final int BATCH_COUNT = 5;
//当一个临时缓存器 当存储器中数据超过BATCH_COUNT后进行释放
List<MyExcel> list=new ArrayList<MyExcel>();
/**
* 如果要存储到数据库 则在这里接入dao 层
*
*/
private MyDao myDao;
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
*/
public DemoDataListener(DemoDAO demoDAO) {
this.demoDAO = demoDAO;
}
/**
* 这个每一条数据解析都会来调用
* @param myExcel
* @param analysisContext
*/
public void invoke(MyExcel myExcel, AnalysisContext analysisContext) {
System.out.println("解析到一条数据"+JSON.toJSONString(myExcel));
LOGGER.info("解析到一条数据", JSON.toJSONString(myExcel));
list.add(myExcel);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
list.clear();
}
}
/**
* 所有数据解析完成了 都会来调用
* @param analysisContext
*/
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
saveData();
LOGGER.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*
* */
private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size());
demoDAO.save(list); //dao层方法 存储到数据库
LOGGER.info("存储数据库成功!");
}
}
**执行 **
String fileName="C:/Users/风筝/OneDrive/桌面/easyExcel.xlsx";
/**
* 文件路径 模板实体类 监听器
*/
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
//方法1
EasyExcel.read(fileName,MyExcel.class,new MyExcelListener()).sheet().doRead();
//方法2
ExcelReader excelReader = EasyExcel.read(fileName, MyExcel.class, new MyExcelListener()).build();
ReadSheet readSheet = EasyExcel.readSheet(0).build();
excelReader.read(readSheet);
// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
excelReader.finish();
有个很重要的点 这里的 监听器 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
存入到持久层
/**
* 假设这个是DAO存储。
**/
public class DemoDAO {
public void save(List<DemoData> list) {
// mybatis,尽量别直接调用多次insert,自己写一个mapper里面新增一个方法batchInsert,所有数据一次性插入
}
}
指定列的下标或名称
/**
* 强制读取第三个 不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配
*/
@ExcelProperty(index = 0)
private String province;
/**
*
* 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据
*
*/
@ExcelProperty("2000年人口数(万人)")
private Integer personNum;
// 接收百分比的数字
@NumberFormat("#.##")
private String percent;
读多个sheet
/**
* 读多个或者全部sheet,这里注意一个sheet不能读取多次,多次读取需要重新读取文件
* <p>
* 1. 创建excel对应的实体对象 参照{@link DemoData}
* <p>
* 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}
* <p>
* 3. 直接读即可
*/
@Test
public void repeatedRead() {
String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
// 读取全部sheet
// 这里需要注意 DemoDataListener的doAfterAllAnalysed 会在每个sheet读取完毕后调用一次。然后所有sheet都会往同一个DemoDataListener里面写
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).doReadAll();
// 读取部分sheet
fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
ExcelReader excelReader = EasyExcel.read(fileName).build();
// 这里为了简单 所以注册了 同样的head 和Listener 自己使用功能必须不同的Listener
ReadSheet readSheet1 =
EasyExcel.readSheet(0).head(DemoData.class).registerReadListener(new DemoDataListener()).build();
ReadSheet readSheet2 =
EasyExcel.readSheet(1).head(DemoData.class).registerReadListener(new DemoDataListener()).build();
// 这里注意 一定要把sheet1 sheet2 一起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能
excelReader.read(readSheet1, readSheet2);
// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
excelReader.finish();
}
- 一个sheet 不能读取多次
- 监听器的doAfterAllAnalysed 会在每个sheet读取完毕后调用一次
- 读取部分sheet时 根据自己需求 定义不同的监听器 每个sheet调用自己的监听器
- 读取时要把所有的sheet一起都放入一个
excelReader.read
自定义类型转换(
converter = MyConverter.class
)
/**
* 强制读取第三个 不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配
*/
@ExcelProperty(index = 0, converter = MyConverter.class )
private String province;
/**
*
* 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据
*
*/
@ExcelProperty("2000年人口数(万人)")
private Integer personNum;
// 接收百分比的数字
@NumberFormat("#.##")
private String percent;
自定义转换器类 实现converter接口
public class MyConverter implements Converter<String> {
//要转换成的java类型
@Override
public Class supportJavaTypeKey() {
return String.class;
}
//将转换的Excel类型
@Override
public CellDataTypeEnum supportExcelTypeKey() {
return CellDataTypeEnum.STRING;
}
/**
* 读的时候会调用
* @param cellData excel表格里的值
* @param excelContentProperty
* @param globalConfiguration
* @return
* @throws Exception
*/
@Override
public String convertToJavaData(CellData cellData, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {
return "自定义的类型"+cellData.getStringValue();
}
/**
* 写的时候会调用
* @param s
* @param excelContentProperty
* @param globalConfiguration
* @return
* @throws Exception
*/
@Override
public CellData convertToExcelData(String s, ExcelContentProperty excelContentProperty, GlobalConfiguration globalConfiguration) throws Exception {
return new CellData(s);
}
}
读取表头数据
重写监听器的 invokeHeadMap方法
/**
* 获取表每一列的名字
* @param headMap
* @param context
*/
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
System.out.println(headMap);
//{0=地区, 1=2000年人口数(万人), 2=2000年比重}
}
web中的读
/**
* 文件上传
* <p>
* 1. 创建excel对应的实体对象 参照{@link UploadData}
* <p>
* 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link UploadDataListener}
* <p>
* 3. 直接读即可
*/
@PostMapping("upload")
@ResponseBody
public String upload(MultipartFile file) throws IOException {
EasyExcel.read(file.getInputStream(), UploadData.class, new UploadDataListener(uploadDAO)).sheet().doRead();
return "success";
}
读的基本步骤
根据要读取的Excel表 创建与之对应的java实体类
实体类属性可用注解
ExcelProperty
指定当前字段对应excel中的那一列。可以根据名字或者Index去匹配。当然也可以不写,默认第一个字段就是index=0,以此类推。千万注意,要么全部不写,要么全部用index,要么全部用名字去匹配。千万别三个混着用,除非你非常了解源代码中三个混着用怎么去排序的。ExcelIgnore
默认所有字段都会和excel去匹配,加了这个注解会忽略该字段DateTimeFormat
日期转换,用String
去接收excel日期格式的数据会调用这个注解。里面的value
参照java.text.SimpleDateFormat
NumberFormat
数字转换,用String
去接收excel数字格式的数据会调用这个注解。里面的value
参照java.text.DecimalFormat
ExcelIgnoreUnannotated
默认不加ExcelProperty
的注解的都会参与读写,加了不会参与参数
ReadWorkbook(理解成excel对象)参数
excelType
当前excel的类型 默认会自动判断
inputStream
与file
二选一。读取文件的流,如果接收到的是流就只用,不用流建议使用file
参数。因为使用了inputStream
easyexcel会帮忙创建临时文件,最终还是file
file
与inputStream
二选一。读取文件的文件。
autoCloseStream
自动关闭流。
readCache
默认小于5M用 内存,超过5M会使用EhCache
,这里不建议使用这个参数。
useDefaultListener
@since 2.1.4
默认会加入ModelBuildEventListener
来帮忙转换成传入class
的对象,设置成false
后将不会协助转换对象,自定义的监听器会接收到Map
对象,如果还想继续接听到class
对象,请调用readListener
方法,加入自定义的beforeListener
、ModelBuildEventListener
、 自定义的afterListener
即可ReadSheet(就是excel的一个Sheet)参数
sheetNo
需要读取Sheet的编码,建议使用这个来指定读取哪个Sheet
sheetName
根据名字去匹配Sheet,excel 2003不支持根据名字去匹配创建一个读的监听器类 继承
AnalysisEventListener<Excel实体类>
- 读取Excel 是按照一行一行进行读取 每次读取完毕都会调用监听器的invoke方法
- 在invoke方法中可以对Excel数据进行逻辑操作
- 接入DAO层后也可以在监听器中调用DAO方法将数据放入数据库
- 在存入数据库中的时候尽量一次性插入完毕 不要一条一条数据进行插入
- 监听器不能由spring托管 每读取一行都要创建一个新的监听器对象
使用EasyExcel类的read方法进行读取
EasyExcel 入口类,用于构建开始各种操作
ExcelReaderBuilder ExcelWriterBuilder 构建出一个 ReadWorkbook WriteWorkbook,可以理解成一个excel对象,一个excel只要构建一个
ExcelReaderSheetBuilder ExcelWriterSheetBuilder 构建出一个 ReadSheet WriteSheet对象,可以理解成excel里面的一页,每一页都要构建一个
ReadListener 在每一行读取完毕后都会调用ReadListener来处理数据
WriteHandler 在每一个操作包括创建单元格、创建表格等都会调用WriteHandler来处理数据
所有配置都是继承的,Workbook的配置会被Sheet继承,所以在用EasyExcel设置参数的时候,在EasyExcel…sheet()方法之前作用域是整个sheet,之后针对单个sheet
EasyExcel.read(inputStream/pathName,head,readListener) /** * Build excel the read * * @param inputStream (要么为) * 文件输入流 * @param pathName(要么为) * 文件路径 * @param head * Excel对应实体类 * @param readListener * 读取Excel的监听器 * @return 读取到的Excel bulid */ //构建Excel对象 1. ExcelReader excel = EasyExcel.read(fileName, MyExcel.class, new MyExcelListener()).build(); EasyExcel.readSheet(sheetNo, sheetName) /** * Build excel the 'readSheet' * * @param sheetNo * Excel中sheet的下标,从0开始 * @param sheetName * Excel中sheet的名字 * @return Excel sheet */ //构建上面Excel里的sheet对象 2.ReadSheet sheet = EasyExcel.readSheet(0).build(); //读取Excel 3.excel.read(sheet) //这里每读取一行都会调用监听器 //这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的 4.excel.finish()
写Excel
Excel对应实体类
public class DemoData {
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("日期标题")
private Date date;
@ExcelProperty("数字标题")
private Double doubleData;
/**
* 忽略这个字段
*/
@ExcelIgnore
private String ignore;
public String getString() {
return string;
}
public void setString(String string) {
this.string = string;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public Double getDoubleData() {
return doubleData;
}
public void setDoubleData(Double doubleData) {
this.doubleData = doubleData;
}
public String getIgnore() {
return ignore;
}
public void setIgnore(String ignore) {
this.ignore = ignore;
}
}
写入Excel
public static void main(String[] args) {
String fileName="C:/Users/风筝/OneDrive/桌面/easyExcelWrite.xlsx";
EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
// 写法2
// 这里 需要指定写用哪个class去写
// ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build();
// WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
// excelWriter.write(data(), writeSheet);
// // 千万别忘记finish 会帮忙关闭流
// excelWriter.finish();
}
public static List<DemoData> data() {
List<DemoData> list = new ArrayList<DemoData>();
Double sum=new Double(0);
for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
data.setString("字符串" + i);
data.setDate(new Date());
data.setDoubleData(0.56);
list.add(data);
sum+=0.56;
}
DemoData data = new DemoData();
data.setDoubleData(sum);
list.add(data);
return list;
}
写入后的Excel
字符串标题 | 日期标题 | 数字标题 |
---|---|---|
字符串0 | 2020-04-24 00:22:10 | 0.56 |
字符串1 | 2020-04-24 00:22:10 | 0.56 |
字符串2 | 2020-04-24 00:22:10 | 0.56 |
字符串3 | 2020-04-24 00:22:10 | 0.56 |
字符串4 | 2020-04-24 00:22:10 | 0.56 |
字符串5 | 2020-04-24 00:22:10 | 0.56 |
字符串6 | 2020-04-24 00:22:10 | 0.56 |
字符串7 | 2020-04-24 00:22:10 | 0.56 |
字符串8 | 2020-04-24 00:22:10 | 0.56 |
字符串9 | 2020-04-24 00:22:10 | 0.56 |
5.6 |
根据参数只导出指定列
public void excludeOrIncludeWrite() {
String fileName = TestFileUtil.getPath() + "excludeOrIncludeWrite" + System.currentTimeMillis() + ".xlsx";
// 根据用户传入字段 假设我们要忽略 date
Set<String> excludeColumnFiledNames = new HashSet<String>();
excludeColumnFiledNames.add("date");
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcel.write(fileName, DemoData.class).excludeColumnFiledNames(excludeColumnFiledNames).sheet("模板")
.doWrite(data());
fileName = TestFileUtil.getPath() + "excludeOrIncludeWrite" + System.currentTimeMillis() + ".xlsx";
// 根据用户传入字段 假设我们只要导出 date
Set<String> includeColumnFiledNames = new HashSet<String>();
includeColumnFiledNames.add("date");
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcel.write(fileName, DemoData.class).includeColumnFiledNames(includeColumnFiledNames).sheet("模板")
.doWrite(data());
}
指定写入的列(
Excel中下标从1开始java中下标从0开始
)
public class IndexData {
@ExcelProperty(value = "字符串标题", index = 0) //对应表中第一列
private String string;
@ExcelProperty(value = "日期标题", index = 1)//对应表中第二列
private Date date;
/**
* 这里设置3 会导致第二列空的
*/
@ExcelProperty(value = "数字标题", index = 3)//对应表中第三列
private Double doubleData;
}
复杂头写入
public class ComplexHeadData {
@ExcelProperty({"主标题", "字符串标题"})
private String string;
@ExcelProperty({"主标题", "日期标题"})
private Date date;
@ExcelProperty({"主标题", "数字标题"})
private Double doubleData;
重复多次写入(写到单个或者多个Sheet)
public void repeatedWrite() {
-----------------------------------------------------------------------------------------------
// 方法1 如果写到同一个sheet
String fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去写
ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build();
// 这里注意 如果同一个sheet只要创建一次
WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
// 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来
for (int i = 0; i < 5; i++) {
// 分页去数据库查询数据 这里可以去数据库查询每一页的数据
List<DemoData> data = data();
excelWriter.write(data, writeSheet);
}
// 千万别忘记finish 会帮忙关闭流
excelWriter.finish();
----------------------------------------------------------------------------------------------
// 方法2 如果写到不同的sheet 同一个对象
fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 指定文件
excelWriter = EasyExcel.write(fileName, DemoData.class).build();
// 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面
for (int i = 0; i < 5; i++) {
// 每次都要创建writeSheet 这里注意必须指定sheetNo
writeSheet = EasyExcel.writerSheet(i, "模板").build();
// 分页去数据库查询数据 这里可以去数据库查询每一页的数据
List<DemoData> data = data();
excelWriter.write(data, writeSheet);
}
// 千万别忘记finish 会帮忙关闭流
excelWriter.finish();
----------------------------------------------------------------------------------------------
// 方法3 如果写到不同的sheet 不同的对象
fileName = TestFileUtil.getPath() + "repeatedWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 指定文件
excelWriter = EasyExcel.write(fileName).build();
// 去调用写入,这里我调用了五次,实际使用时根据数据库分页的总的页数来。这里最终会写到5个sheet里面
for (int i = 0; i < 5; i++) {
// 每次都要创建writeSheet 这里注意必须指定sheetNo。这里注意DemoData.class 可以每次都变,我这里为了方便 所以用的同一个class 实际上可以一直变
writeSheet = EasyExcel.writerSheet(i, "模板").head(DemoData.class).build();
// 分页去数据库查询数据 这里可以去数据库查询每一页的数据
List<DemoData> data = data();
excelWriter.write(data, writeSheet);
}
// 千万别忘记finish 会帮忙关闭流
excelWriter.finish();
}
日期、数字或者自定义格式转换
public class ConverterData {
/**
* 所有的 字符串起前面加上"自定义:"三个字
*/
@ExcelProperty(value = "字符串标题", converter = CustomStringStringConverter.class)
private String string;
/**
* 我想写到excel 用年月日的格式
*/
@DateTimeFormat("yyyy年MM月dd日HH时mm分ss秒")
@ExcelProperty("日期标题")
private Date date;
/**
* 我想写到excel 用百分比表示
*/
@NumberFormat("#.##%")
@ExcelProperty(value = "数字标题")
private Double doubleData;
合并单元格
public void mergeWrite() {
String fileName = TestFileUtil.getPath() + "mergeWrite" + System.currentTimeMillis() + ".xlsx";
// 每隔2行会合并 把eachColumn 设置成 3 也就是我们数据的长度,所以就第一列会合并。当然其他合并策略也可以自己写
LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(2, 0);
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
EasyExcel.write(fileName, DemoData.class).registerWriteHandler(loopMergeStrategy).sheet("模板")
.doWrite(data());
}
自定义合并规则
-
首先自定义一个类继承
AbstractMergeStrategy
-
继承后会有一个merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex)方法
- sheet是当前操作cel所在l的sheet
- cell为当前操作cell 此方法每一个cell进行写出时都会进行调用
- head当前sheet的每一列的头标题
-
创建CellRangeAddress对象添加合并规则(
注意要进行条件判断 确保合并后的cell不再与其他cell进行合并
写一个cell 都会调用此方法)-
CellRangeAddress cellRangeAddress = new CellRangeAddress(firstRowIndex, lastRowIndex, firstColumnIndex, lastColumnIndex);
-
-
将合并后的cell添加到sheet中
-
sheet.addMergedRegionUnsafe(cellRangeAddress);
-
系统自带方法
:OnceAbsoluteMergeStrategy
/**
*
*
*
* Excel 中 下标从1开始 java中下标从0开始
* int firstRowIndex, 起始表格所在行
* int lastRowIndex, 终止表格所在行
* int firstColumnIndex, 起始表格所在列
* int lastColumnIndex 终止表格所在列
*/
public OnceAbsoluteMergeStrategy(int firstRowIndex, int lastRowIndex, int firstColumnIndex, int lastColumnIndex) {
if (firstRowIndex < 0 || lastRowIndex < 0 || firstColumnIndex < 0 || lastColumnIndex < 0) {
throw new IllegalArgumentException("All parameters must be greater than 0");
}
this.firstRowIndex = firstRowIndex;
this.lastRowIndex = lastRowIndex;
this.firstColumnIndex = firstColumnIndex;
this.lastColumnIndex = lastColumnIndex;
}
系统自带方法
LoopMergeStrategy
/**
*
* Excel 中 下标从1开始 java中下标从0开始
* int eachRow, 合并行数
* int columnCount, 合并列数 默认为1
* int columnIndex 表格中的第几列
*
*
**/
public LoopMergeStrategy(int eachRow, int columnCount, int columnIndex) {
if (eachRow < 1) {
throw new IllegalArgumentException("EachRows must be greater than 1");
}
if (columnCount < 1) {
throw new IllegalArgumentException("ColumnCount must be greater than 1");
}
if (columnCount == 1 && eachRow == 1) {
throw new IllegalArgumentException("ColumnCount or eachRows must be greater than 1");
}
if (columnIndex < 0) {
throw new IllegalArgumentException("ColumnIndex must be greater than 0");
}
this.eachRow = eachRow;
this.columnCount = columnCount;
this.columnIndex = columnIndex;
}
web中的写
/**
* 文件下载(失败了会返回一个有部分数据的Excel)
* <p>
* 1. 创建excel对应的实体对象 参照{@link DownloadData}
* <p>
* 2. 设置返回的 参数
* <p>
* 3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大
*/
@GetMapping("download")
public void download(HttpServletResponse response) throws IOException {
// 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode("测试", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data());
------------------------------------------------------------------------------------------
/**
* 文件下载并且失败的时候返回json(默认失败了会返回一个有部分数据的Excel)
*
* @since 2.1.1
*/
@GetMapping("downloadFailedUsingJson")
public void downloadFailedUsingJson(HttpServletResponse response) throws IOException {
// 这里注意 使用swagger 会导致各种问题,请直接用浏览器或者用postman
try {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode("测试", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
// 这里需要设置不关闭流
EasyExcel.write(response.getOutputStream(), DownloadData.class).autoCloseStream(Boolean.FALSE).sheet("模板")
.doWrite(data());
} catch (Exception e) {
// 重置response
response.reset();
response.setContentType("application/json");
response.setCharacterEncoding("utf-8");
Map<String, String> map = new HashMap<String, String>();
map.put("status", "failure");
map.put("message", "下载文件失败" + e.getMessage());
response.getWriter().println(JSON.toJSONString(map));
}
}
写的基本步骤
创建Excel对应java实体类
实体类可用注解
ExcelProperty
index 指定写到第几列,默认根据成员变量排序。value
指定写入的名称,默认成员变量的名字,多个value
可以参照快速开始中的复杂头ExcelIgnore
默认所有字段都会写入excel,这个注解会忽略这个字段DateTimeFormat
日期转换,将Date
写到excel会调用这个注解。里面的value
参照java.text.SimpleDateFormat
NumberFormat
数字转换,用Number
写excel会调用这个注解。里面的value
参照java.text.DecimalFormat
ExcelIgnoreUnannotated
默认不加ExcelProperty
的注解的都会参与读写,加了不会参与参数
通用参数
WriteWorkbook
,WriteSheet
,WriteTable
都会有的参数,如果为空,默认使用上级。
converter
转换器,默认加载了很多转换器。也可以自定义。writeHandler
写的处理器。可以实现WorkbookWriteHandler
,SheetWriteHandler
,RowWriteHandler
,CellWriteHandler
,在写入excel的不同阶段会调用relativeHeadRowIndex
距离多少行后开始。也就是开头空几行needHead
是否导出头head
与clazz
二选一。写入文件的头列表,建议使用class。clazz
与head
二选一。写入文件的头对应的class,也可以使用注解。autoTrim
字符串、表头等数据自动trimWriteWorkbook(理解成excel对象)参数
excelType
当前excel的类型 默认xlsx
outputStream
与file
二选一。写入文件的流file
与outputStream
二选一。写入的文件templateInputStream
模板的文件流templateFile
模板文件autoCloseStream
自动关闭流。password
写的时候是否需要使用密码useDefaultStyle
写的时候是否是使用默认头WriteSheet(就是excel的一个Sheet)参数
sheetNo
需要写入的编码。默认0sheetName
需要些的Sheet名称,默认同sheetNo
将数据写入表 数据需要以List
的方式传入write() 使用EasyExcel类的write进行写入
- EasyExcel 入口类,用于构建开始各种操作
- ExcelReaderBuilder ExcelWriterBuilder 构建出一个 ReadWorkbook WriteWorkbook,可以理解成一个excel对象,一个excel只要构建一个
- ExcelReaderSheetBuilder ExcelWriterSheetBuilder 构建出一个 ReadSheet WriteSheet对象,可以理解成excel里面的一页,每一页都要构建一个
- ReadListener 在每一行读取完毕后都会调用ReadListener来处理数据
- WriteHandler 在每一个操作包括创建单元格、创建表格等都会调用WriteHandler来处理数据
- 所有配置都是继承的,Workbook的配置会被Sheet继承,所以在用EasyExcel设置参数的时候,在EasyExcel…sheet()方法之前作用域是整个sheet,之后针对单个sheet
//1.EasyExcel.write()创建Excel对象 ExcelWriter excel = EasyExcel.write(fileName, DemoData.class).build() //2.EasyExcel.writerSheet(“”sheet名字); 创建sheet对象 /** * writerSheet(Integer sheetNo, String sheetName) * 一次写入多个sheet时须指定 sheetNo **/ WriteSheet sheet =EasyExcel.sheet("模板").build(); //3. excel.writerSheet(data(), sheet); 写入 data()的返回值需要时List<T> excel.write(data(), sheet); //4. 千万别忘记finish 会帮忙关闭流 excelWriter.finish();