当前位置: 首页 > news >正文

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循环可以遍历数组&#xff0c;它一共有三个参数&#xff0c;第一个参数可以当成数组索引值&#xff0c;想要遍历时候可以设置初始值为0&#xff0c;然后以数组长度为判断依据&#xff0c;如果不…...

Python程序设计基础2

第1关:HUT开学了: # 请在此添加代码 Name = input() # 输入学生的姓名 ########## Begin ########## print("|++++++++++++++++++++++|") print("| |") print("| Welcome to HUT |") print("| …...

域名服务器有哪些类型

域名服务器有哪些类型 随着现在网络的不断发展&#xff0c;越来越多的企业开始使用网络建站&#xff0c;以此来进行营销和推广&#xff0c;而网站在建设过程中需要使用域名和空间&#xff0c;那么域名服务器是什么&#xff1f;下面由给大家说一下。 主域名服务器 负责维护一…...

5.什么是Spring的依赖注入(DI)?IOC和DI的区别是什么

很多人把IOC和DI说成一个东西&#xff0c;笼统来说的话是没有问题的&#xff0c;但是本质上还是有所区别的,希望大家能够严谨一点&#xff0c; IOC和DI是从不同的角度描述的同一件事&#xff0c;IOC是从容器的角度描述&#xff0c;而DI是从应用程序的角度来描述&#xff0c;也…...

Python开源自动化工具Playwright安装及介绍

一个非常强大的自动化项目叫 playwright-python 它支持主流的浏览器&#xff0c;包含&#xff1a;Chrome、Firefox、Safari、Microsoft Edge 等&#xff0c;同时支持以无头模式、有头模式运行&#xff0c;并提供了同步、异步的 API&#xff0c;可以结合 Pytest 测试框架 使用&…...

Nginx生产环境安装配置

不建议使用nginx-1.18.0.tar.gz&#xff0c;因为扫出很多漏洞 上传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 语法&#xff0c;希望了解 go 项目的构建过程和方式。 项目信息 go-gin-example 项目是使用 gin…...

基于轻量级yolov5的瓷砖瑕疵检测系统

该专栏仅支持购买本专栏的同学学习使用,不支持以超级会员、VIP等形式使用,请谅解!【购买专栏后可选择其中一个完整源码项目】 本文是我新开设的专栏《完整源码项目实战》 的第十二篇全源码文章,包含数据集在内的所有资源,可以实现零基础上手入门学习。前面系列文章链接如下…...

Linux:系统基本信息扫描(2)

#网络地址:ip a------------------------------------------------------------- ip a echo "主机名:-------------------------------------------------------" hostnamectl sleep 0.5s echo "#系统基本信息:--------------------------------------------…...

什么是虚拟DOM(Virtual DOM),说说工作原理

虚拟DOM&#xff08;Virtual DOM&#xff09;是一种将页面的状态抽象为JavaScript对象表示的概念&#xff0c;用于提高Web应用程序的性能和渲染效率。 虚拟DOM的工作原理如下&#xff1a; 1&#xff1a; 初始渲染&#xff1a;首先&#xff0c;通过JavaScript对象&#xff08;…...

Kafka的重要组件,谈谈流处理引擎Kafka Stream

系列文章目录 上手第一关&#xff0c;手把手教你安装kafka与可视化工具kafka-eagle Kafka是什么&#xff0c;以及如何使用SpringBoot对接Kafka 架构必备能力——kafka的选型对比及应用场景 Kafka存取原理与实现分析&#xff0c;打破面试难关 防止消息丢失与消息重复——Kafka可…...

基于yolov5模型的200种鸟类检测识别分析系统

该专栏仅支持购买本专栏的同学学习使用,不支持以超级会员、VIP等形式使用,请谅解!【购买专栏后可选择其中一个完整源码项目】 本文是我新开设的专栏《完整源码项目实战》 的第十三篇全源码文章,包含数据集在内的所有资源,可以实现零基础上手入门学习。前面系列文章链接如下…...

JavaScript的学习,就这一篇就OK了!(超详细)

目录 Day27 JavaScript(1) 1、JS的引入方式 2、ECMAScript基本语法 3、ECMAScript 基本数据类型​编辑 3.1 数字类型 3.2 字符串 3.3 布尔值 3.4 空值&#xff08;Undefined和Null&#xff09; 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 中的表达式&#xff0c;比如变量、函数调用、方法调用等。 if、switch、变量声明等属于语句&#xff0c;不是表达式。 列表渲染使用 map 。 事件绑定用&#xff1b;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.前后端分离&#xff1a; 2.多数据源&#xff1a; 3.预置功能&#xff1a; 4.私有化部署&#xff1a; 四、总结 可视化低代码…...

数据分析思维与模型:多维度拆解分析法

多维度拆解分析法"&#xff08;Multi-Dimensional Analysis and Decomposition Method&#xff09;是一种用于深入分析和解决复杂问题的方法论。这种方法侧重于从多个角度或维度来考察问题&#xff0c;以便于更全面地理解和解决它们。它通常包括以下几个步骤&#xff1a; …...

Goby 漏洞发布|大华智慧园区综合管理平台 poi 文件上传漏洞

漏洞名称&#xff1a;大华智慧园区综合管理平台 poi 文件上传漏洞 English Name&#xff1a;Dahua Smart Park Integrated Management Platform poi file upload vulnerability CVSS core:9.0 影响资产数&#xff1a;7113 漏洞描述&#xff1a; 大华智慧园区综合管理平台是…...

【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密

在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案

问题描述&#xff1a;iview使用table 中type: "index",分页之后 &#xff0c;索引还是从1开始&#xff0c;试过绑定后台返回数据的id, 这种方法可行&#xff0c;就是后台返回数据的每个页面id都不完全是按照从1开始的升序&#xff0c;因此百度了下&#xff0c;找到了…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

成都鼎讯硬核科技!雷达目标与干扰模拟器,以卓越性能制胜电磁频谱战

在现代战争中&#xff0c;电磁频谱已成为继陆、海、空、天之后的 “第五维战场”&#xff0c;雷达作为电磁频谱领域的关键装备&#xff0c;其干扰与抗干扰能力的较量&#xff0c;直接影响着战争的胜负走向。由成都鼎讯科技匠心打造的雷达目标与干扰模拟器&#xff0c;凭借数字射…...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计&#xff0c;提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合&#xff1a;各模块职责清晰&#xff0c;便于独立开发…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)

船舶制造装配管理现状&#xff1a;装配工作依赖人工经验&#xff0c;装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书&#xff0c;但在实际执行中&#xff0c;工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官

。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量&#xff1a;setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...