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 漏洞描述: 大华智慧园区综合管理平台是…...
FFmpeg 低延迟同屏方案
引言 在实时互动需求激增的当下,无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作,还是游戏直播的画面实时传输,低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架,凭借其灵活的编解码、数据…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...
C++八股 —— 单例模式
文章目录 1. 基本概念2. 设计要点3. 实现方式4. 详解懒汉模式 1. 基本概念 线程安全(Thread Safety) 线程安全是指在多线程环境下,某个函数、类或代码片段能够被多个线程同时调用时,仍能保证数据的一致性和逻辑的正确性…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

[论文阅读]TrustRAG: Enhancing Robustness and Trustworthiness in RAG
TrustRAG: Enhancing Robustness and Trustworthiness in RAG [2501.00879] TrustRAG: Enhancing Robustness and Trustworthiness in Retrieval-Augmented Generation 代码:HuichiZhou/TrustRAG: Code for "TrustRAG: Enhancing Robustness and Trustworthin…...