SpringBoot整合EasyExcel
目录
一、EasyExcel介绍
1、简介
2、常用注解
二、SpringBoot整合EasyExcel
1、基本环境
(1)引入依赖
(2)创建实体类
2、EasyExcel内容读取
(1)创建监听器
(2)测试
3、EasyExcel内容校验
(1)场景描述
(2)EasyExcel监听器
(3)测试
4、EasyExcel内容批量插入数据库
(1)创建数据库
(2)编写Mapper
(3)编写Service
(4)Controller层
(5)自定义转换器
(6)EasyExcel监听器
(7)测试
5、EasyExcel写入文件(基础版)
(1)写入操作
(2)测试结果
6、EasyExcel写入文件(Web模式基础版)
(1)Web模式写入操作
(2)测试结果
7、EasyExcel写入文件(Web模式进阶版)
(1)Web模式写入操作进阶版
(2)测试结果
8、EasyExcel写入操作(批量写入)
(1)Mapper文件
(2)编写Service
(3)Controller层
(4)测试结果
一、EasyExcel介绍
1、简介
EasyExcel是阿里巴巴开源poi插件之一,是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。尽管Java解析、生成Excel不止EasyExcel,还有一些其他的框架,例如:Apache poi、jxl,但这些都存在一个严重的问题就是非常的耗内存。而EasyExcel是在尽可能节约内存的情况下支持读写百M的Excel,能在不用考虑性能、内存的等因素的情况下,快速完成Excel的读、写等功能。因此,本文将通过一个书籍列表的导入,作为案例,来揭开EasyExcel的使用过程。

2、常用注解
- @ExcelProperty:指定当前字段对应excel中的那一列。可以根据名字或者Index去匹配。当然也可以不写,默认第一个字段就是index=0,以此类推。千万注意,要么全部不写,要么全部用index,要么全部用名字去匹配。千万别三个混着用,除非你非常了解源代码中三个混着用怎么去排序的。
- @ExcelIgnore:默认所有字段都会和excel去匹配,加了这个注解会忽略该字段
- @DateTimeFormat:日期转换,用String去接收excel日期格式的数据会调用这个注解。里面的value参照java.text.SimpleDateFormat
- @NumberFormat:数字转换,用String去接收excel数字格式的数据会调用这个注解。里面的value参照java.text.DecimalFormat
- @ExcelIgnoreUnannotated:默认不加ExcelProperty 的注解的都会参与读写,加了不会参与
二、SpringBoot整合EasyExcel
1、基本环境
(1)引入依赖
<!-- 查看excel的maven仓库 https://mvnrepository.com/artifact/com.alibaba/easyexcel --><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.1.2</version></dependency>
(2)创建实体类
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;import java.math.BigDecimal;@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Book {@ExcelProperty(value = "发布日期")@DateTimeFormat("yyyy-MM-dd")private String publishDate;@ExcelProperty("书名")private String bookName;@ExcelProperty("作者")private String author;@ExcelProperty("价格")private BigDecimal price;@ExcelProperty("售卖数量")private Integer saleNum;@ExcelProperty(value = "是否热卖")private String status;
}
2、EasyExcel内容读取
(1)创建监听器
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.example.yht.entity.Book;import java.util.*;public class ExcelListener extends AnalysisEventListener<Book> {@Overridepublic void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {System.out.println("解析表头信息");System.out.println(headMap);}@Overridepublic void onException(Exception exception, AnalysisContext context) {System.out.println("Error Message : " + exception);}@Overridepublic void invoke(Book book, AnalysisContext analysisContext) {System.out.println(book);}@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {System.out.println("解析完毕");}@Overridepublic boolean hasNext(AnalysisContext context) {return super.hasNext(context);}
}
(2)测试
主方法:
public static void main(String[] args) {String filename="D:\\其他\\书籍列表.xlsx";System.out.println("Excel内容如下:");ExcelListener excelListener = new ExcelListener();EasyExcel.read(filename, Book.class, excelListener).sheet(0).doRead();}
测试结果:

3、EasyExcel内容校验
(1)场景描述
上面介绍的内容只是一个简单的Excel读取案例。接下来,我们可以在解析加一些校验规则,例如:书籍的发布日期格式必须为YYYY-MM-DD格式;价格的整数位不能超过10位,小数位不能超过三位。接下来就来改造ExcelListener,以实现我们的需求。
(2)EasyExcel监听器
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.alibaba.excel.exception.ExcelAnalysisStopException;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.example.yht.entity.Book;import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;public class ExcelListener extends AnalysisEventListener<Book> {private List<Book> list = new ArrayList<>();private List<String> errList= new ArrayList<>();public List<Book> getList() {return list;}public void setList(List<Book> list) {this.list = list;}public List<String> getErrList() {return errList;}public void setErrList(List<String> errList) {this.errList = errList;}public int getRow() {return row;}public void setRow(int row) {this.row = row;}public int getCol() {return col;}public void setCol(int col) {this.col = col;}public Map<Integer, String> getErrorMessageMap() {return errorMessageMap;}private int row = 1;private int col = 1;private final Map<Integer, String> errorMessageMap = new HashMap<>();@Overridepublic void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {System.out.println("解析表头信息");System.out.println(headMap);row++;}@Overridepublic void onException(Exception exception, AnalysisContext context) {String errMsg = "第" + row + "行, 第" + col + "列解析失败";errList.add(errMsg);row++ ;if(exception instanceof ExcelAnalysisException) {throw new ExcelAnalysisStopException("解析失败!");}else{throw new ExcelAnalysisStopException("解析失败!");}}@Overridepublic void invoke(Book book, AnalysisContext analysisContext) {if(book == null){return;}if(!preDealEntity(book)){throw new ExcelAnalysisException("数据解析错误");}list.add(book);row++;}public boolean preDealEntity(Book book){if(null == book){return false;}Field[] fields = book.getClass().getDeclaredFields();col = 1;for(int i=0; i < fields.length; i++){Field field = fields[i];field.setAccessible(true);try {Object val = field.get(book);if(val != null){if(field.getName().equals("publishDate") && !isValidDate((String)val, "yyyy-MM-dd")){return false;}if(val instanceof BigDecimal && !isNumeric(val+"")){return false;}}col++;} catch (IllegalAccessException e) {e.printStackTrace();}}return true;}public static boolean isNumeric(String str) {if (null == str || "".equals(str)) {return false;}String regx = "[+-]*\\d+\\.?\\d*[Ee]*[+-]*\\d+";Pattern pattern = Pattern.compile(regx);boolean isNumber = pattern.matcher(str).matches();if (!isNumber) {return isNumber;}regx = "^[-\\+]?[.\\d]*$";pattern = Pattern.compile(regx);isNumber = pattern.matcher(str).matches();if (!isNumber) {return isNumber;}String[] arrs = str.split("\\.");return arrs[0].length() <= 10 && arrs[1].length() <= 3;}public static boolean isValidDate(String dateStr, String format) {SimpleDateFormat sdf = new SimpleDateFormat(format);sdf.setLenient(false); // 设置解析日期时严格匹配,不容忍格式错误try {sdf.parse(dateStr);return true;} catch (ParseException e) {return false;}}@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {System.out.println("解析完毕");}@Overridepublic boolean hasNext(AnalysisContext context) {return super.hasNext(context);}
}
(3)测试
主方法:
public static void main(String[] args) {//SpringApplication.run(FtpDemoApplication.class);String filename="D:\\其他\\书籍列表.xlsx";System.out.println("Excel内容如下:");ExcelListener excelListener = new ExcelListener();EasyExcel.read(filename, Book.class, excelListener).sheet(0).doRead();if(excelListener.getErrList().isEmpty()){System.out.println("所有数据解析成功!");System.out.println(excelListener.getList());}else{System.out.println("数据解析失败:");System.out.println(excelListener.getErrList());}}
第一种情况:解析正常的情况
Excel内容如下:
21:05:22.869 [main] DEBUG com.alibaba.excel.metadata.property.ExcelHeadProperty - The initialization sheet/table 'ExcelHeadProperty' is complete , head kind is CLASS
21:05:22.891 [main] DEBUG com.alibaba.excel.context.AnalysisContextImpl - Initialization 'AnalysisContextImpl' complete
21:05:23.018 [main] DEBUG com.alibaba.excel.cache.selector.SimpleReadCacheSelector - Use map cache.size:590
21:05:23.227 [main] DEBUG com.alibaba.excel.metadata.property.ExcelHeadProperty - The initialization sheet/table 'ExcelHeadProperty' is complete , head kind is CLASS
21:05:23.227 [main] DEBUG com.alibaba.excel.context.AnalysisContextImpl - Began to read:com.alibaba.excel.read.metadata.holder.xlsx.XlsxReadSheetHolder@6a021575
解析表头信息
{0=发布日期, 1=书名, 2=作者, 3=价格, 4=售卖数量, 5=是否热卖}
解析完毕
所有数据解析成功!
[Book(publishDate=2013-5-4, bookName=三国演义, author=罗贯中, price=24.15, saleNum=34, status=是), Book(publishDate=2016-2-14, bookName=水浒传, author=施耐庵, price=49.34, saleNum=23, status=否), Book(publishDate=2013-12-14, bookName=三国演义, author=罗贯中, price=24.15, saleNum=1, status=是)]Process finished with exit code 0
在表格中添加一些错误数据,如下:


可以看到:上面的代码案例中,如果某一行解析失败,则剩余行不做解析,直接返回解析错误的信息。
还有另外一种错误情况:如果某一行解析失败,剩余行依旧需要解析,直到解析完毕返回所有的错误信息。那么可以将监听器代码改成下面这样:
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.alibaba.excel.exception.ExcelAnalysisStopException;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.example.yht.entity.Book;import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;public class ExcelListener extends AnalysisEventListener<Book> {private List<Book> list = new ArrayList<>();private List<String> errList= new ArrayList<>();public List<Book> getList() {return list;}public void setList(List<Book> list) {this.list = list;}public List<String> getErrList() {return errList;}public void setErrList(List<String> errList) {this.errList = errList;}public int getRow() {return row;}public void setRow(int row) {this.row = row;}public int getCol() {return col;}public void setCol(int col) {this.col = col;}public Map<Integer, String> getErrorMessageMap() {return errorMessageMap;}private int row = 1;private int col = 1;private final Map<Integer, String> errorMessageMap = new HashMap<>();@Overridepublic void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {System.out.println("解析表头信息");System.out.println(headMap);row++;}@Overridepublic void onException(Exception exception, AnalysisContext context) {String errMsg = "第" + row + "行, 第" + col + "列解析失败";row++ ; }@Overridepublic void invoke(Book book, AnalysisContext analysisContext) {if(book == null){return;}preDealEntity(book);list.add(book);row++;}public void preDealEntity(Book book){if(null == book){return;}Field[] fields = book.getClass().getDeclaredFields();col = 1;for(int i=0; i < fields.length; i++){Field field = fields[i];field.setAccessible(true);try {Object val = field.get(book);if(val != null){if(field.getName().equals("publishDate") && !isValidDate((String)val, "yyyy-MM-dd")){String errMsg = "第" + row + "行, 第" + col + "列解析失败";errList.add(errMsg);}if(val instanceof BigDecimal && !isNumeric(val+"")){String errMsg = "第" + row + "行, 第" + col + "列解析失败";errList.add(errMsg);}}col++;} catch (IllegalAccessException e) {e.printStackTrace();}}}public static boolean isNumeric(String str) {if (null == str || "".equals(str)) {return false;}String regx = "[+-]*\\d+\\.?\\d*[Ee]*[+-]*\\d+";Pattern pattern = Pattern.compile(regx);boolean isNumber = pattern.matcher(str).matches();if (!isNumber) {return isNumber;}regx = "^[-\\+]?[.\\d]*$";pattern = Pattern.compile(regx);isNumber = pattern.matcher(str).matches();if (!isNumber) {return isNumber;}String[] arrs = str.split("\\.");return arrs[0].length() <= 10 && arrs[1].length() <= 3;}public static boolean isValidDate(String dateStr, String format) {SimpleDateFormat sdf = new SimpleDateFormat(format);sdf.setLenient(false); // 设置解析日期时严格匹配,不容忍格式错误try {sdf.parse(dateStr);return true;} catch (ParseException e) {return false;}}@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {System.out.println("解析完毕");}@Overridepublic boolean hasNext(AnalysisContext context) {return super.hasNext(context);}
}


4、EasyExcel内容批量插入数据库
(1)创建数据库
create table t_book(publish_date date,book_name varchar(50),author varchar(20),price decimal(10,3),sale_num int,status tinyint(1)
);
(2)编写Mapper
@Mapper
public interface BookMapper {Integer saveBook(@Param("bookList") List<Book> bookList);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!--namespace:指定了唯一的命名空间-->
<mapper namespace="com.example.yht.mapper.BookMapper"><insert id="saveBook">insert into t_book(publish_date, book_name, author, price, sale_num, status)VALUES<foreach collection="bookList" index="" item="book" separator=",">(#{book.publishDate},#{book.bookName},#{book.author},#{book.price},#{book.saleNum},#{book.status})</foreach></insert>
</mapper>
(3)编写Service
public interface IBookService {Integer saveBook(List<Book> bookList);
}
@Service
public class BookServiceImpl implements IBookService {@Autowiredprivate BookMapper bookMapper;@Overridepublic Integer saveBook(List<Book> bookList) {return bookMapper.saveBook(bookList);}
}
(4)Controller层
import com.alibaba.excel.EasyExcel;
import com.example.yht.entity.Book;
import com.example.yht.entity.Student;
import com.example.yht.listener.ExcelListener;
import com.example.yht.service.IBookService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;import java.text.SimpleDateFormat;@RestController
public class TestController {@Autowiredprivate IBookService bookService;@GetMapping("/excelToDB")public void excelToDB(){String filename="D:\\其他\\书籍列表.xlsx";System.out.println("Excel内容如下:");ExcelListener excelListener = new ExcelListener(bookService);EasyExcel.read(filename, Book.class, excelListener).sheet(0).doRead();if(excelListener.getErrList().isEmpty()){System.out.println("所有数据解析成功!");}else{System.out.println("数据解析失败:");System.out.println(excelListener.getErrList());}}
}
(5)自定义转换器
从Excel中可以看到一个细节,是否热卖这一列在excel里面的值有两种:是/否。但是在数据库里面,status的类型却是tinyint。因此我们就需要将excel里面的是/否转换为1/0,从而存储在数据库中。这里我们介绍一个Excel的转换器,可以实现这项操作。
这里首先将Book的实体类中的status改为Integer类型:
@ExcelProperty(value = "是否热卖", converter = StatusConverter.class)private Integer status;
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.converters.ReadConverterContext;
import com.alibaba.excel.converters.WriteConverterContext;
import com.alibaba.excel.enums.CellDataTypeEnum;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;public class StatusConverter implements Converter<Integer> {@Overridepublic Class<?> supportJavaTypeKey() {return Converter.super.supportJavaTypeKey();}@Overridepublic CellDataTypeEnum supportExcelTypeKey() {return Converter.super.supportExcelTypeKey();}@Overridepublic Integer convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {return Converter.super.convertToJavaData(cellData, contentProperty, globalConfiguration);}@Overridepublic Integer convertToJavaData(ReadConverterContext<?> context) throws Exception {return context.getReadCellData().getStringValue().equals("是") ? 1 : 0;}@Overridepublic WriteCellData<?> convertToExcelData(Integer value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) throws Exception {return new WriteCellData<Integer>(value.equals(1) ? "是" : "否");}}
(6)EasyExcel监听器
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.exception.ExcelAnalysisException;
import com.alibaba.excel.exception.ExcelAnalysisStopException;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.util.ListUtils;
import com.example.yht.entity.Book;
import com.example.yht.service.IBookService;import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;public class ExcelListener extends AnalysisEventListener<Book> {//假设每5条插入一次数据库private static int BATCH_COUNT = 5;private List<Book> bookList = new ArrayList<>();private List<String> errList= new ArrayList<>();private IBookService bookService;public ExcelListener(IBookService bookService) {this.bookService = bookService;}public List<Book> getBookList() {return bookList;}public void setBookList(List<Book> bookList) {this.bookList = bookList;}public List<String> getErrList() {return errList;}public void setErrList(List<String> errList) {this.errList = errList;}public int getRow() {return row;}public void setRow(int row) {this.row = row;}public int getCol() {return col;}public void setCol(int col) {this.col = col;}public Map<Integer, String> getErrorMessageMap() {return errorMessageMap;}private int row = 1;private int col = 1;private final Map<Integer, String> errorMessageMap = new HashMap<>();@Overridepublic void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {System.out.println("解析表头信息");System.out.println(headMap);row++;}@Overridepublic void onException(Exception exception, AnalysisContext context) {String errMsg = "第" + row + "行, 第" + col + "列解析失败";errList.add(errMsg);System.out.println(exception);row++ ;if(exception instanceof ExcelAnalysisException) {throw new ExcelAnalysisStopException("解析失败!");}else{throw new ExcelAnalysisStopException("解析失败!");}}@Overridepublic void invoke(Book book, AnalysisContext analysisContext) {if(book == null){return;}if(!preDealEntity(book)){throw new ExcelAnalysisException("数据解析错误");}bookList.add(book);if(bookList.size() >= BATCH_COUNT){saveData(bookList);}row++;}/*** 保存数据* @param list*/private void saveData(List<Book> list) {bookService.saveBook(list);bookList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);}public boolean preDealEntity(Book book){if(null == book){return false;}Field[] fields = book.getClass().getDeclaredFields();col = 1;for(int i=0; i < fields.length; i++){Field field = fields[i];field.setAccessible(true);try {Object val = field.get(book);if(val != null){if(field.getName().equals("publishDate") && !isValidDate((String)val, "yyyy-MM-dd")){return false;}if(val instanceof BigDecimal && !isNumeric(val+"")){return false;}}col++;} catch (IllegalAccessException e) {e.printStackTrace();}}return true;}public static boolean isNumeric(String str) {if (null == str || "".equals(str)) {return false;}String regx = "[+-]*\\d+\\.?\\d*[Ee]*[+-]*\\d+";Pattern pattern = Pattern.compile(regx);boolean isNumber = pattern.matcher(str).matches();if (!isNumber) {return isNumber;}regx = "^[-\\+]?[.\\d]*$";pattern = Pattern.compile(regx);isNumber = pattern.matcher(str).matches();if (!isNumber) {return isNumber;}String[] arrs = str.split("\\.");return arrs[0].length() <= 10 && arrs[1].length() <= 3;}public static boolean isValidDate(String dateStr, String format) {SimpleDateFormat sdf = new SimpleDateFormat(format);sdf.setLenient(false); // 设置解析日期时严格匹配,不容忍格式错误try {sdf.parse(dateStr);return true;} catch (ParseException e) {return false;}}@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {System.out.println("解析完毕");//解析完之后,将list中的数据保存在数据库中saveData(bookList);}@Overridepublic boolean hasNext(AnalysisContext context) {return super.hasNext(context);}
}
(7)测试
页面请求:http://localhost:8800/excelToDB
终端输出:

数据库内容:

5、EasyExcel写入文件(基础版)
(1)写入操作
这里先看一个基本的案例,首先指定文件位置,并添加一些模拟数据,再通过write方法写入该文件中。
@GetMapping("/writeBasic")public void writeBasic(){String fileName = "D:\\Data\\easyexcel.xlsx";List<Book> list = new ArrayList<>();list.add(new Book("2022-12-1", "人物传记1", "迪丽热巴1", new BigDecimal("34.5"), 20, 1));list.add(new Book("2022-12-2", "人物传记2", "迪丽热巴2", new BigDecimal("44.5"), 30, 0));list.add(new Book("2022-12-3", "人物传记3", "迪丽热巴3", new BigDecimal("54.5"), 40, 1));EasyExcel.write(fileName, Book.class).sheet("模板").doWrite(list);}
(2)测试结果
浏览器请求:http://localhost:8800/writeBasic

6、EasyExcel写入文件(Web模式基础版)
(1)Web模式写入操作
Web模式的写入操作,可以在页面发起请求的时候,将数据内容写入到excel中,并下载到本地。代码如下:
@GetMapping("/doWrite")public void download(HttpServletResponse response) throws IOException {// 这里直接用浏览器或者用postmanresponse.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");response.setCharacterEncoding("utf-8");String fileName = URLEncoder.encode("书籍列表.xlsx", "UTF-8").replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);//模拟要写入excel的数据List<Book> list = new ArrayList<>();list.add(new Book("2023-1-1", "花朵1", "张清1", new BigDecimal("34.5"), 20, 1));list.add(new Book("2023-1-2", "花朵2", "张清2", new BigDecimal("44.5"), 30, 0));list.add(new Book("2023-1-3", "花朵3", "张清3", new BigDecimal("54.5"), 40, 1));EasyExcel.write(response.getOutputStream(), Book.class).sheet("新增").doWrite(list);}
(2)测试结果
浏览器发起请求:http://localhost:8800/doWrite
文件下载:

表格内容:

7、EasyExcel写入文件(Web模式进阶版)
(1)Web模式写入操作进阶版
这里的进阶版是在基础班的上面,加入了文件流的关闭操作等。
@GetMapping("/doWrite2")public void downloadAdvanced(HttpServletResponse response){//模拟写入的数据List<Book> list = new ArrayList<>();list.add(new Book("2023-2-1", "碧螺春1", "肖战1", new BigDecimal("34.5"), 20, 1));list.add(new Book("2023-2-2", "碧螺春2", "肖战2", new BigDecimal("44.5"), 30, 0));list.add(new Book("2023-2-3", "碧螺春3", "肖战3", new BigDecimal("54.5"), 40, 1));try {response.setContentType("application/vnd.ms-excel");response.setCharacterEncoding("utf-8");//防止中文乱码String fileName = URLEncoder.encode("书籍列表-new.xlsx", "UTF-8").replaceAll("\\+", "%20");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);ServletOutputStream outputStream = response.getOutputStream();//工作簿对象ExcelWriterBuilder writerWork = EasyExcel.write(outputStream, Book.class);//工作表对象ExcelWriterSheetBuilder sheet = writerWork.sheet(0);sheet.doWrite(list);outputStream.flush();outputStream.close();} catch (Exception e) {// 重置responseresponse.reset();response.setContentType("application/json");response.setCharacterEncoding("utf-8");Map<String, String> map = new HashMap<>();map.put("status", "failure");map.put("message", "下载文件失败" + e.getMessage());e.printStackTrace();}}
(2)测试结果
浏览器发起请求:http://localhost:8800/doWrite2


8、EasyExcel写入操作(批量写入)
既然我们在插入数据库的时候,有批量插入的功能,那么自然也可以实现批量写入excel的方式。这里我们就从数据库里将插入的数据再次读 取出来,放入一个新的excel表格中。
(1)Mapper文件
<select id="selectCount" resultType="Integer">select count(*) from t_book</select><select id="selectBatch" resultType="com.example.yht.entity.Book" parameterType="map">selectpublish_date as publishDate,book_name as bookName,author as author,price as price,sale_num as saleNum,status as statusfrom t_book limit ${limitVal} offset ${offsetVal}</select>
Integer selectCount();List<Book> selectBatch(Map<String, Object> map);
(2)编写Service
Integer selectCount();List<Book> selectBatch(Map<String, Object> map);
@Overridepublic Integer selectCount() {return bookMapper.selectCount();}@Overridepublic List<Book> selectBatch(Map<String, Object> map) {return bookMapper.selectBatch(map);}
(3)Controller层
这里为了验证批量插入的效果,在每次读取之后,写入不同的sheet。
@GetMapping("/doWriteBatch")public void downloadBatch(HttpServletResponse response){// 数据的总量(要写入Excel文件中的数据)int count = bookService.selectCount();int batchCount = 5;List<Book> dataList = new ArrayList<>(batchCount);// 总Sheet数int num = count / batchCount;// 输出流OutputStream outputStream = null;try {response.setCharacterEncoding("UTF-8");String excelName = URLEncoder.encode("batch_book", "UTF-8");response.setHeader("Content-disposition", "attachment;filename=" + excelName + ".xlsx");// 以字节流的形式输出响应正文outputStream = response.getOutputStream();try (ExcelWriter excelWriter = EasyExcel.write(outputStream, Book.class).build()) {// 调用写入for (int i = 0; i <= num; i++) {// 每次写入都要创建WriteSheet, 这里注意必须指定sheetNo, 并且sheetName必须不一样Map<String, Object> queryMap = new HashMap<>();queryMap.put("limitVal", batchCount);queryMap.put("offsetVal", i * batchCount);WriteSheet writeSheet = EasyExcel.writerSheet(i, "batch" + (i + 1)).build();dataList = bookService.selectBatch(queryMap);System.out.println(dataList);excelWriter.write(dataList, writeSheet);}}// 清空集合dataList.clear();} catch (IOException ex) {throw new RuntimeException(ex);} finally {if (outputStream != null) {try {outputStream.close();} catch (IOException ex) {throw new RuntimeException(ex);}}}}
(4)测试结果
请求:http://localhost:8800/doWriteBatch




相关文章:
SpringBoot整合EasyExcel
目录 一、EasyExcel介绍 1、简介 2、常用注解 二、SpringBoot整合EasyExcel 1、基本环境 (1)引入依赖 (2)创建实体类 2、EasyExcel内容读取 (1)创建监听器 (2)测试 3、EasyExcel内容校验 (1)场景描述 (2)EasyExcel监听器 (3)测试 4、EasyExcel内容批量插…...
详解JS遍历数组的十八种方法
for循环 let arr[1,2,3] for(let i0;i<arr.length;i){console.log(arr[i]) }for循环可以遍历数组,它一共有三个参数,第一个参数可以当成数组索引值,想要遍历时候可以设置初始值为0,然后以数组长度为判断依据,如果不…...
Python程序设计基础2
第1关:HUT开学了: # 请在此添加代码 Name = input() # 输入学生的姓名 ########## Begin ########## print("|++++++++++++++++++++++|") print("| |") print("| Welcome to HUT |") print("| …...
域名服务器有哪些类型
域名服务器有哪些类型 随着现在网络的不断发展,越来越多的企业开始使用网络建站,以此来进行营销和推广,而网站在建设过程中需要使用域名和空间,那么域名服务器是什么?下面由给大家说一下。 主域名服务器 负责维护一…...
5.什么是Spring的依赖注入(DI)?IOC和DI的区别是什么
很多人把IOC和DI说成一个东西,笼统来说的话是没有问题的,但是本质上还是有所区别的,希望大家能够严谨一点, IOC和DI是从不同的角度描述的同一件事,IOC是从容器的角度描述,而DI是从应用程序的角度来描述,也…...
Python开源自动化工具Playwright安装及介绍
一个非常强大的自动化项目叫 playwright-python 它支持主流的浏览器,包含:Chrome、Firefox、Safari、Microsoft Edge 等,同时支持以无头模式、有头模式运行,并提供了同步、异步的 API,可以结合 Pytest 测试框架 使用&…...
Nginx生产环境安装配置
不建议使用nginx-1.18.0.tar.gz,因为扫出很多漏洞 上传nginx-1.24.0.tar.gz [rootzonghe01 data]# ll -rw-r--r-- 1 root root 1112471 Oct 26 15:57 nginx-1.24.0.tar.gz [rootzonghe01 data]# pwd /data解押 [rootzonghe01 data]# tar -zxvf nginx-1.24.0.tar…...
「项目阅读系列」go-gin-example star 6.5k!(1)
文章目录 准备工作适宜人群项目信息 项目结构代码阅读主要模块代码主函数模块router 路由模块auth 授权模块数据库 修改文章请求分析其他依赖 总结 准备工作 适宜人群 初学 go 语法,希望了解 go 项目的构建过程和方式。 项目信息 go-gin-example 项目是使用 gin…...
基于轻量级yolov5的瓷砖瑕疵检测系统
该专栏仅支持购买本专栏的同学学习使用,不支持以超级会员、VIP等形式使用,请谅解!【购买专栏后可选择其中一个完整源码项目】 本文是我新开设的专栏《完整源码项目实战》 的第十二篇全源码文章,包含数据集在内的所有资源,可以实现零基础上手入门学习。前面系列文章链接如下…...
Linux:系统基本信息扫描(2)
#网络地址:ip a------------------------------------------------------------- ip a echo "主机名:-------------------------------------------------------" hostnamectl sleep 0.5s echo "#系统基本信息:--------------------------------------------…...
什么是虚拟DOM(Virtual DOM),说说工作原理
虚拟DOM(Virtual DOM)是一种将页面的状态抽象为JavaScript对象表示的概念,用于提高Web应用程序的性能和渲染效率。 虚拟DOM的工作原理如下: 1: 初始渲染:首先,通过JavaScript对象(…...
Kafka的重要组件,谈谈流处理引擎Kafka Stream
系列文章目录 上手第一关,手把手教你安装kafka与可视化工具kafka-eagle Kafka是什么,以及如何使用SpringBoot对接Kafka 架构必备能力——kafka的选型对比及应用场景 Kafka存取原理与实现分析,打破面试难关 防止消息丢失与消息重复——Kafka可…...
基于yolov5模型的200种鸟类检测识别分析系统
该专栏仅支持购买本专栏的同学学习使用,不支持以超级会员、VIP等形式使用,请谅解!【购买专栏后可选择其中一个完整源码项目】 本文是我新开设的专栏《完整源码项目实战》 的第十三篇全源码文章,包含数据集在内的所有资源,可以实现零基础上手入门学习。前面系列文章链接如下…...
JavaScript的学习,就这一篇就OK了!(超详细)
目录 Day27 JavaScript(1) 1、JS的引入方式 2、ECMAScript基本语法 3、ECMAScript 基本数据类型编辑 3.1 数字类型 3.2 字符串 3.3 布尔值 3.4 空值(Undefined和Null) 3.5 类型转换 3.6 原始值和引用值 4、运算符 5、流程控制语句 5.1 分…...
hive sql 取当周周一 str_to_date(DATE_FORMAT(biz_date, ‘%Y%v‘), ‘%Y%v‘)
select str_to_date(DATE_FORMAT(biz_date, %Y%v), %Y%v)方法拆解 select DATE_FORMAT(now(), %Y%v), str_to_date(202346, %Y%v)...
【React】React 基础
1. 搭建环境 npx create-react-app react-basic-demo2. 基本使用 JSX 中使用 {} 识别 JavaScript 中的表达式,比如变量、函数调用、方法调用等。 if、switch、变量声明等属于语句,不是表达式。 列表渲染使用 map 。 事件绑定用;on 事件名称…...
CentOS7 设置 nacos 开机启动
1、新增服务文件 vim /lib/systemd/system/nacos.service2、增加如下内容 [Unit] Descriptionnacos Afternetwork.target[Service] Typeforking ExecStart/usr/local/nacos/bin/startup.sh -m standalone ExecReload/usr/local/nacos/bin/shutdown.sh ExecStop/usr/local/nac…...
使用低代码可视化开发平台快速搭建应用
目录 一、JNPF可视化平台介绍 二、搭建JNPF可视化平台 【表单设计】 【报表设计】 【流程设计】 【代码生成器】 三、使用JNPF可视化平台 1.前后端分离: 2.多数据源: 3.预置功能: 4.私有化部署: 四、总结 可视化低代码…...
数据分析思维与模型:多维度拆解分析法
多维度拆解分析法"(Multi-Dimensional Analysis and Decomposition Method)是一种用于深入分析和解决复杂问题的方法论。这种方法侧重于从多个角度或维度来考察问题,以便于更全面地理解和解决它们。它通常包括以下几个步骤: …...
Goby 漏洞发布|大华智慧园区综合管理平台 poi 文件上传漏洞
漏洞名称:大华智慧园区综合管理平台 poi 文件上传漏洞 English Name:Dahua Smart Park Integrated Management Platform poi file upload vulnerability CVSS core:9.0 影响资产数:7113 漏洞描述: 大华智慧园区综合管理平台是…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
12.找到字符串中所有字母异位词
🧠 题目解析 题目描述: 给定两个字符串 s 和 p,找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义: 若两个字符串包含的字符种类和出现次数完全相同,顺序无所谓,则互为…...
HTML前端开发:JavaScript 常用事件详解
作为前端开发的核心,JavaScript 事件是用户与网页交互的基础。以下是常见事件的详细说明和用法示例: 1. onclick - 点击事件 当元素被单击时触发(左键点击) button.onclick function() {alert("按钮被点击了!&…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
Qt 事件处理中 return 的深入解析
Qt 事件处理中 return 的深入解析 在 Qt 事件处理中,return 语句的使用是另一个关键概念,它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别:不同层级的事件处理 方…...
【Kafka】Kafka从入门到实战:构建高吞吐量分布式消息系统
Kafka从入门到实战:构建高吞吐量分布式消息系统 一、Kafka概述 Apache Kafka是一个分布式流处理平台,最初由LinkedIn开发,后成为Apache顶级项目。它被设计用于高吞吐量、低延迟的消息处理,能够处理来自多个生产者的海量数据,并将这些数据实时传递给消费者。 Kafka核心特…...
React父子组件通信:Props怎么用?如何从父组件向子组件传递数据?
系列回顾: 在上一篇《React核心概念:State是什么?》中,我们学习了如何使用useState让一个组件拥有自己的内部数据(State),并通过一个计数器案例,实现了组件的自我更新。这很棒&#…...
工厂方法模式和抽象工厂方法模式的battle
1.案例直接上手 在这个案例里面,我们会实现这个普通的工厂方法,并且对比这个普通工厂方法和我们直接创建对象的差别在哪里,为什么需要一个工厂: 下面的这个是我们的这个案例里面涉及到的接口和对应的实现类: 两个发…...
