当前位置: 首页 > 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; 大华智慧园区综合管理平台是…...

可靠性+灵活性:电力载波技术在楼宇自控中的核心价值

可靠性灵活性&#xff1a;电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中&#xff0c;电力载波技术&#xff08;PLC&#xff09;凭借其独特的优势&#xff0c;正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据&#xff0c;无需额外布…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

关键领域软件测试的突围之路:如何破解安全与效率的平衡难题

在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的核心战斗力。不同于普通商业软件&#xff0c;这些承载着国家安全使命的软件系统面临着前所未有的质量挑战——如何在确保绝对安全的前提下&#xff0c;实现高效测试与快速迭代&#xff1f;这一命题正考验着…...

Fabric V2.5 通用溯源系统——增加图片上传与下载功能

fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...

iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈

在日常iOS开发过程中&#xff0c;性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期&#xff0c;开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发&#xff0c;但背后往往隐藏着系统资源调度不当…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

&#x1f4cc; LRU 缓存机制详解与实现&#xff08;Java版&#xff09; 一、&#x1f4d6; 问题背景 在日常开发中&#xff0c;我们经常会使用 缓存&#xff08;Cache&#xff09; 来提升性能。但由于内存有限&#xff0c;缓存不可能无限增长&#xff0c;于是需要策略决定&am…...

群晖NAS如何在虚拟机创建飞牛NAS

套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...

实战三:开发网页端界面完成黑白视频转为彩色视频

​一、需求描述 设计一个简单的视频上色应用&#xff0c;用户可以通过网页界面上传黑白视频&#xff0c;系统会自动将其转换为彩色视频。整个过程对用户来说非常简单直观&#xff0c;不需要了解技术细节。 效果图 ​二、实现思路 总体思路&#xff1a; 用户通过Gradio界面上…...

【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL

ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...