EasyExcel

EasyExcel依赖

<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.5</version></dependency>

EasyExcel相关网站

  • 官网:https://easyexcel.opensource.alibaba.com/
  • Github:https://github.com/alibaba/easyexcel
  • 3.0.5版本的JavaDoc:https://javadoc.io/doc/com.alibaba/easyexcel/3.0.5/index.html

EasyExcel简单读取Excel文件

前期准备

  • 准备Excel文件

  • 准备Excel文件对应的实体类:Student
    package com.study.easyexcel.entity;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.util.Date;@Data@AllArgsConstructor@NoArgsConstructorpublic class Student {/** * id */private String id;/** * 学生姓名 */private String name;/** * 学生出生日期 */private Date birthday;/** * 学生性别 */private String gender;}

编写代码-简单读取一个Excel中的Sheet的数据

  • 注意:在没有任何配置的情况下,Excel中的数据列顺序要与实体类的属性顺序保持一致

BasicRead-主代码

package com.study.easyexcel.basic;import com.alibaba.excel.EasyExcel;import com.alibaba.excel.read.builder.ExcelReaderBuilder;import com.alibaba.excel.read.builder.ExcelReaderSheetBuilder;import com.study.easyexcel.basic.listener.StudentListener;import com.study.easyexcel.entity.Student;import java.io.InputStream;public class BasicRead {public static void main(String[] args) {//获取到要读取的Excel文件的输入流InputStream excelFileIs = BasicRead.class.getResourceAsStream("/excel/学员信息表.xlsx");//获取一个工作簿对象/* * @param inputStream:表示要读取的 Excel 文件的输入流。可以从文件、网络、数据库等地方获取。 * @param head:表示要读取的 Excel 文件中的表头信息所对应的数据模型类型。可以是 Java 类型、Map 类型或 List 类型。 * @param readListener:表示读取 Excel 文件时的数据监听器(ReadListener)实例,用于在读取过程中接收读取到的数据,并在读取过程中执行相应的操作。 */ExcelReaderBuilder readWorkerBook = EasyExcel.read(excelFileIs, Student.class, new StudentListener());//获取一个工作簿对象ExcelReaderSheetBuilder sheet = readWorkerBook.sheet();//读取工作表中的内容sheet.doRead();}}

StudentListener-继承了AnalysisEventListener的读监听类

package com.study.easyexcel.basic.listener;import com.alibaba.excel.context.AnalysisContext;import com.alibaba.excel.event.AnalysisEventListener;import com.alibaba.excel.read.metadata.holder.ReadRowHolder;import com.study.easyexcel.entity.Student;public class StudentListener extends AnalysisEventListener<Student> {/** * 在读取到 Excel 文件中的一行数据时会被调用 * * @param data读取到的一行数据,可以是 Java 对象、Map、List 等类型的数据 * @param context 参数表示当前读取的上下文信息,包括当前 Sheet 页的信息、当前行号、表头行号等。 */@Overridepublic void invoke(Student data, AnalysisContext context) {//获取上下文信息ReadRowHolder readRowHolder = context.readRowHolder();Integer rowIndex = readRowHolder.getRowIndex();System.out.println("读取到了第 " + rowIndex + " 行数据 --> " + data);}/** * 在读取 Excel 文件完成后会被调用 * * @param context 表示当前读取的上下文信息,包括当前 Sheet 页的信息、总行数等。 */@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {System.out.println("读取完毕!");}}

读取效果

EasyExcel简单写入Excel文件-默认样式

编写代码:BasicWrite

package com.study.easyexcel.basic;import com.alibaba.excel.EasyExcel;import com.alibaba.excel.write.builder.ExcelWriterBuilder;import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;import com.study.easyexcel.entity.Student;import java.util.ArrayList;import java.util.Date;import java.util.List;public class BasicWrite {public static void main(String[] args) {//设置输出的Excel文件的存储路径String savePath = "F:\\JavaCodeStandalone\\easy-excel-study\\eaey-excel-basic\\src\\main\\resources\\excel\\学员信息表-Write.xlsx";//构建一个写的工作簿对象//如果写入的文件不存在,则会自动创建ExcelWriterBuilder write = EasyExcel.write(savePath, Student.class);//工作表对象ExcelWriterSheetBuilder sheet = write.sheet();//向工作表写入数据sheet.doWrite(getStudentList());}/** * 随机获取10个Student数据 */private static List<Student> getStudentList() {List<Student> list = new ArrayList<>();for (int i = 1; i <= 10; i++) {list.add(new Student(String.valueOf((int) (Math.random() * 1000_0000) + 1000_0000),"张" + i,new Date((long) (System.currentTimeMillis() + Math.random() * 10_0000)),Math.random() < 0.5 " />"男" : "女"));}return list;}}

查看生成的Excel数据

  • 可以发现,默认情况下,使用的列名是属性名,且列宽也不适应数据

EasyExcel简单写入Excel文件-自定义样式(列名、行宽)

  • 在写入Excel文件时,可以使用EasyExcel提供的注解对列名、列顺序、行高、列宽等进行设置
  • 写入Excel的代码还是编写代码:BasicWrite类中的数据,只需要调整实体类的注解即可

修改实体类Student,添加相应的注解

package com.study.easyexcel.entity;import com.alibaba.excel.annotation.ExcelProperty;import com.alibaba.excel.annotation.write.style.ColumnWidth;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.util.Date;@Data@AllArgsConstructor@NoArgsConstructorpublic class Student {/** * id */@ExcelProperty(value = "学生Id", index = 0)//设置列名和列顺序@ColumnWidth(value = 10)//设置列宽private String id;/** * 学生姓名 */@ExcelProperty(value = "学生姓名", index = 1)@ColumnWidth(value = 15)private String name;/** * 学生出生日期 */@ExcelProperty(value = "学生出生日期", index = 2)@ColumnWidth(value = 25)private Date birthday;/** * 学生性别 */@ExcelProperty(value = "学生性别", index = 3)@ColumnWidth(value = 15)private String gender;}

查看写出的Excel文件样式


EasyExcel+SpringMVC简单的上传下载Excel文件

  • 准备一个普通的Webapp项目,然后进行配置:web.xml配置文件
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version="4.0"><context-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring.xml</param-value></context-param><listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener><servlet><servlet-name>dispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring-mvc.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>dispatcherServlet</servlet-name><url-pattern>/</url-pattern></servlet-mapping><filter><filter-name>encodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param><init-param><param-name>forceEncoding</param-name><param-value>true</param-value></init-param></filter><filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping></web-app>
  • resources目录配置文件:spring.xml
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd"><context:component-scan base-package="com.study.easyexcel"><context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/></context:component-scan></beans>
  • resources目录配置文件:spring-mvc.xml
    <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"><context:component-scan base-package="com.study.easyexcel.controller"/><bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver"id="multipartResolver"/><mvc:annotation-driven><mvc:message-converters><bean class="org.springframework.http.converter.StringHttpMessageConverter"><constructor-arg value="UTF-8"/></bean></mvc:message-converters></mvc:annotation-driven></beans>
  • controller层代码:StudentController
    package com.study.easyexcel.controller;import com.alibaba.excel.EasyExcel;import com.study.easyexcel.entity.Student;import com.study.easyexcel.listener.WebStudentListener;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import org.springframework.web.multipart.MultipartFile;import javax.servlet.ServletOutputStream;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.net.URLEncoder;import java.nio.charset.StandardCharsets;import java.util.ArrayList;import java.util.Date;import java.util.List;@RestController@RequestMapping("/student")public class StudentController {@AutowiredWebStudentListener webStudentListener;/** * 上传Excel */@RequestMapping("/upload")public String uploadExcel(MultipartFile uploadExcel) {try {EasyExcel.read(uploadExcel.getInputStream(), Student.class, webStudentListener).sheet().doRead();return "上传成功";} catch (IOException e) {e.printStackTrace();return "上传失败";}}/** * 下载Excel */@RequestMapping("/download")public void downloadExcel(HttpServletResponse response) throws IOException {//设置响应类型response.setContentType("application/vnd.ms-excel");//设置响应编码response.setCharacterEncoding(StandardCharsets.UTF_8.toString());//将文件名进行URL编码String fileName = URLEncoder.encode("学员数据", "UTF-8");//设置如何显示响应内容:inline->直接浏览、attachment->附件下载response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + fileName + ".xlsx");//获取响应给客户端的输出流ServletOutputStream outputStream = response.getOutputStream();EasyExcel.write(outputStream, Student.class).sheet().doWrite(getStudentList());}/** * 随机获取10个Student数据 */private static List<Student> getStudentList() {List<Student> list = new ArrayList<>();for (int i = 1; i <= 10; i++) {list.add(new Student(String.valueOf((int) (Math.random() * 1000_0000) + 1000_0000),"张" + i,new Date((long) (System.currentTimeMillis() + Math.random() * 10_0000)),Math.random() < 0.5 ? "男" : "女",Math.random() * 1_0000,"信息工程学院"));}return list;}}
  • 编写Excel读取所需的Linstener:com.study.easyexcel.listener.WebStudentListener
    package com.study.easyexcel.listener;import com.alibaba.excel.context.AnalysisContext;import com.alibaba.excel.event.AnalysisEventListener;import com.study.easyexcel.entity.Student;import com.study.easyexcel.service.IStudentService;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Scope;import org.springframework.stereotype.Component;import java.util.ArrayList;import java.util.List;@Component@Scope("prototype")//Listener不要配置成单例模式public class WebStudentListener extends AnalysisEventListener<Student> {@Autowiredprivate IStudentService studentService;private List<Student> list = new ArrayList<>();@Overridepublic void invoke(Student data, AnalysisContext context) {list.add(data);//每5条消息处理一次if(list.size() % 5==0){studentService.save(list);list.clear();}}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {}}
  • 编写service接口:com.study.easyexcel.service.IStudentService
    package com.study.easyexcel.service;import com.study.easyexcel.entity.Student;import java.util.List;public interface IStudentService {void save(List<Student> students);}
  • 编写service接口实现类:com.study.easyexcel.service.impl.StudentImpl
    package com.study.easyexcel.service.impl;import com.study.easyexcel.entity.Student;import com.study.easyexcel.service.IStudentService;import org.springframework.stereotype.Service;import java.util.List;@Servicepublic class StudentImpl implements IStudentService {@Overridepublic void save(List<Student> students) {System.out.println("接收到上传解析出来的Excel数据:");students.forEach(System.out::println);}}
  • 上传接口:http://localhost:8080/student/upload
  • 下载接口:http://localhost:8080/student/upload

EasyExcel模板填充-概述

模板填充的语法-单条数据填充

  • 在需要填充的位置使用{属性名}的方式进行占位

  • 如果本身内容就有{}需要对其使用\进行转义,例如:\{name\}

  • 单行填充可以使用实体类或者Map作为填充数据源,那么实体类的属性或者Map的Key名要与Excel模板文件中的占位名称保持一致

  • 如果在一个单元格内需要填充两个数据,那么可以直接写两个占位的,可以是同名的,如果同名,则会重复填充(可以多行,但是只会重复相同的填充数据)

  • 如果希望在填充后的Excel文件中,让\{name\}显示为{name},那么与之一起的必须有一个能被正常填充的占位符

  • 使用{属性名}方式占位时,如果强行传入多条数据,那么最后将不会进行任何数据填充

模板填充的语法-多条数据填充

  • 占位符语法:{.属性名}
  • 多条数据填充只是在属性名前面家里一个.,此时就可以传入一个集合类型的数据,那么最终填充出来的数据就会是整个集合中的数据

EasyExcel模板填充-最简单的单组数据填充-使用实体类填充

  • 准备一个要填充的模板,该模板的样式会在填充后仍然保留

用于填充的实体类-StudentByFill

package com.study.easyexcel.entity;import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;import com.alibaba.excel.annotation.ExcelProperty;import com.alibaba.excel.annotation.format.DateTimeFormat;import com.alibaba.excel.annotation.format.NumberFormat;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.math.RoundingMode;import java.util.Date;@Data@AllArgsConstructor@NoArgsConstructor@ExcelIgnoreUnannotatedpublic class StudentByFill {/** * 学生Id */@ExcelProperty(value = "学生Id")private String id;/** * 学生姓名 */@ExcelProperty(value = "学生姓名")private String name;/** * 学生出生日期 */@DateTimeFormat(value = "yyyy-MM-dd")@ExcelProperty(value = "学生出生日期")private Date birthday;/** * 学生性别 */@ExcelProperty(value = "学生性别")private String gender;/** * 学费 */@ExcelProperty(value = "学费")@NumberFormat(value = "#.00", roundingMode = RoundingMode.HALF_UP)private Double tuition;/** * 学生院系 */@ExcelProperty(value = "学生院系")private String department;}

编写填充代码-OneLineFill

package com.study.easyexcel.fill.oneline;import com.alibaba.excel.EasyExcel;import com.alibaba.excel.write.builder.ExcelWriterBuilder;import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;import com.study.easyexcel.entity.StudentByFill;import java.util.Date;public class OneLineFill {public static void main(String[] args) {//要填充的Excel模板的路径String templatePath = "F:\\JavaCodeStandalone\\easy-excel-study\\eaey-excel-basic\\src\\main\\resources\\excel_template\\简单的单行填充.xlsx";//获取工作簿对象(整个Excel文件)ExcelWriterBuilder workBook = EasyExcel.write("F:\\JavaCodeStandalone\\easy-excel-study\\eaey-excel-basic\\src\\main\\resources\\excel\\简单的单行填充-Write.xlsx", StudentByFill.class)//与模板进行关联.withTemplate(templatePath);//获取Sheet对象ExcelWriterSheetBuilder sheet = workBook.sheet();//创建需要被填充的数据StudentByFill fillData = new StudentByFill("20222410666", "张三", new Date(978567730000L), "男", 21035.00, "信息工程学院");//执行数据填充sheet.doFill(fillData);//do开头的方法会自在操作结束后,自动关闭流}}

填充效果

EasyExcel模板填充-最简单的单组数据填充-使用Map填充

  • 填充模板和属性仍然使用EasyExcel模板填充-最简单的单组数据填充-使用实体类填充中的
  • 这里只将填充方式从实体类填充变为Map填充

编写填充代码-OneLineFillByMap

package com.study.easyexcel.fill.oneline;import com.alibaba.excel.EasyExcel;import com.alibaba.excel.write.builder.ExcelWriterBuilder;import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;import com.study.easyexcel.entity.StudentByFill;import java.util.HashMap;import java.util.Map;public class OneLineFillByMap {public static void main(String[] args) {//要填充的Excel模板的路径String templatePath = "F:\\JavaCodeStandalone\\easy-excel-study\\eaey-excel-basic\\src\\main\\resources\\excel_template\\简单的单行填充.xlsx";//获取工作簿对象(整个Excel文件)ExcelWriterBuilder workBook = EasyExcel.write("F:\\JavaCodeStandalone\\easy-excel-study\\eaey-excel-basic\\src\\main\\resources\\excel\\简单的单行填充-Write.xlsx", StudentByFill.class)//与模板进行关联.withTemplate(templatePath);//获取Sheet对象ExcelWriterSheetBuilder sheet = workBook.sheet();//创建需要被填充的数据Map<String, String> fillData = new HashMap<>();fillData.put("id", "20222410666");fillData.put("name", "张三");//这里要注意,由于日期直接使用了字符串填充,需要注意日期对象转成日期之后的格式//fillData.put("birthday",new Date(978567730000L).toString());//Thu Jan 04 08:22:10 CST 2001fillData.put("birthday", "2001-01-01 11:22:33");//Thu Jan 04 08:22:10 CST 2001fillData.put("gender", "男");fillData.put("tuition", "21035.00");fillData.put("department", "信息工程学院");//执行数据填充sheet.doFill(fillData);}}

填充效果


EasyExcel模板填充-垂直列表填充-使用List填充

  • 准备一个要填充的模板,该模板的样式会在填充后仍然保留

  • 用于填充的实体类-StudentByFill同上

编写填充代码-MultilineFileByList

package com.study.easyexcel.fill.multiline;import com.alibaba.excel.EasyExcel;import com.alibaba.excel.write.builder.ExcelWriterBuilder;import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;import com.study.easyexcel.entity.StudentByFill;import java.util.ArrayList;import java.util.Date;import java.util.List;public class MultilineFileByList {public static void main(String[] args) {//要填充的Excel模板的路径String templatePath = "F:\\JavaCodeStandalone\\easy-excel-study\\eaey-excel-basic\\src\\main\\resources\\excel_template\\多行数据填充模板.xlsx";//填充后文件的保存路径String savePath = "F:\\JavaCodeStandalone\\easy-excel-study\\eaey-excel-basic\\src\\main\\resources\\excel\\多行数据填充-Write.xlsx";//根据模板获取工作簿ExcelWriterBuilder workBook = EasyExcel.write(savePath, StudentByFill.class).withTemplate(templatePath);//获取工作表ExcelWriterSheetBuilder sheet = workBook.sheet();//获取要填充的数据源List<StudentByFill> fillDataList = getFillDataList();//填充模板sheet.doFill(fillDataList);}/** * 生成要用于填充的数据 */public static List<StudentByFill> getFillDataList() {List<StudentByFill> list = new ArrayList<>();for (int i = 1; i <= 10; i++) {list.add(new StudentByFill(String.valueOf((int) (Math.random() * 1000_0000) + 1000_0000), "张" + i, new Date(), Math.random() < 0.5 " />"男" : "女", Math.random() * 1_0000, "信息工程学院"));}return list;}}

填充效果

EasyExcel模板填充-水平列表填充

  • 准备一个要填充的模板,该模板的样式会在填充后仍然保留

  • 用于填充的实体类-StudentByFill同上

编写填充代码-HorizontalFill

package com.study.easyexcel.fill.multiline;import com.alibaba.excel.EasyExcel;import com.alibaba.excel.ExcelWriter;import com.alibaba.excel.enums.WriteDirectionEnum;import com.alibaba.excel.write.metadata.WriteSheet;import com.alibaba.excel.write.metadata.fill.FillConfig;import com.study.easyexcel.entity.StudentByFill;import java.util.ArrayList;import java.util.Date;import java.util.List;public class HorizontalFill {public static void main(String[] args) {//要填充的Excel模板的路径String templatePath = "F:\\JavaCodeStandalone\\easy-excel-study\\eaey-excel-basic\\src\\main\\resources\\excel_template\\水平列表填充模板.xlsx";//填充后文件的保存路径String savePath = "F:\\JavaCodeStandalone\\easy-excel-study\\eaey-excel-basic\\src\\main\\resources\\excel\\水平列表填充-Write.xlsx";//根据模板获取工作簿对象ExcelWriter workBook = EasyExcel.write(savePath, StudentByFill.class).withTemplate(templatePath).build();//获取工作表对象WriteSheet sheet = EasyExcel.writerSheet().build();//构建一个 FillConfig 对象,设置填充方向FillConfig build = FillConfig.builder()//设置填充方向为水平.direction(WriteDirectionEnum.HORIZONTAL).build();//填充数据workBook.fill(getFillDataList(), build, sheet);//调用do开头的方法时,内部会自动关闭流,当调用fill()方法时,需要自己手动关闭流workBook.finish();}/** * 生成要用于填充的数据 */public static List<StudentByFill> getFillDataList() {List<StudentByFill> list = new ArrayList<>();for (int i = 1; i <= 10; i++) {list.add(new StudentByFill(String.valueOf((int) (Math.random() * 1000_0000) + 1000_0000), "张" + i, new Date(), Math.random() < 0.5 " />"男" : "女", Math.random() * 1_0000, "信息工程学院"));}return list;}}

填充效果



EasyExcel模板填充-复杂数据填充

  • 填充列表时,会有多组数据填充和单组数据填充的混合使用。这种情况下就必须手动调用fill()方法进行填充
  • 如果多组数据在单组数据之前填充,那么需要用FillConfig设置多组数据填充时要从新行开始,这样就不会覆盖住底部的汇总数据

Excel模板

代码需要的实体类-Bill

package com.study.easyexcel.entity;import lombok.AllArgsConstructor;import lombok.Data;import java.util.Date;@Data@AllArgsConstructorpublic class Bill {/** * 日期 */private Date date;/** * 收支类型 */private String type;/** * 类别 */private String category;/** * 金额 */private Double amount;/** * 备注 */private String note;}

编写填充代码-AccountingFill

package com.study.easyexcel.fill.complex;import com.alibaba.excel.EasyExcel;import com.alibaba.excel.ExcelWriter;import com.alibaba.excel.write.metadata.WriteSheet;import com.alibaba.excel.write.metadata.fill.FillConfig;import com.study.easyexcel.entity.Bill;import java.text.ParseException;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.HashMap;import java.util.List;public class AccountingFill {public static void main(String[] args) throws ParseException {//要填充的Excel模板的路径String templatePath = "F:\\JavaCodeStandalone\\easy-excel-study\\eaey-excel-basic\\src\\main\\resources\\excel_template\\组合数据填充模板.xlsx";//填充后文件的保存路径String savePath = "F:\\JavaCodeStandalone\\easy-excel-study\\eaey-excel-basic\\src\\main\\resources\\excel\\组合数据填充-Write.xlsx";//工作簿对象ExcelWriter workBook = EasyExcel.write(savePath, Bill.class).withTemplate(templatePath).build();//工作表对象WriteSheet sheet = EasyExcel.writerSheet().build();//配置多组数据填充完后,需要换行,防止覆盖模板中的单组数据模板FillConfig fillConfig = FillConfig.builder().forceNewRow(true).build();//获取要填充的数据List<Bill> bills = getBills();//先填充出来多组数据(账单详情数据)workBook.fill(bills, fillConfig, sheet);//填充单组数据(汇总数据)long totalCount = bills.size();//账单条数double totalAmount = bills.stream().mapToDouble(Bill::getAmount).sum();//账单总金额HashMap<String, String> summaryMap = new HashMap<>();summaryMap.put("totalCount", String.valueOf(totalCount));summaryMap.put("totalAmount", String.valueOf(totalAmount));//手动单独填充单组数据workBook.fill(summaryMap, sheet);//关闭流workBook.finish();}/** * 生成账单数据 */private static List<Bill> getBills() throws ParseException {List<Bill> list = new ArrayList<>();String datas = "2022-10-11\t支出\t购物\t18.36\t这里是备注内容\n" +"2022-10-11\t支出\t餐饮\t6\t这里是备注内容\n" +"2022-10-11\t支出\t学习\t1\t这里是备注内容\n" +"2022-10-11\t支出\t餐饮\t9\t这里是备注内容\n" +"2022-10-11\t支出\t学习\t1.5\t这里是备注内容\n" +"2022-10-10\t支出\t洗澡\t0.6\t这里是备注内容\n" +"2022-10-10\t支出\t餐饮\t6\t这里是备注内容\n" +"2022-10-10\t支出\t网络支出\t1\t这里是备注内容\n" +"2022-10-10\t支出\t餐饮\t9\t这里是备注内容";//分解出每一条数据for (String bill : datas.split("\n")) {String[] bills = bill.split("\t");Bill bill1 = new Bill(new SimpleDateFormat("yyyy-MM-dd").parse(bills[0]),bills[1],bills[2],Double.parseDouble(bills[3]),bills[4]);list.add(bill1);}return list;}}

填充效果