# 一.简单介绍
# 1.需求背景
在项目开发过程中,很多时候需要用到导出功能,将表格中的数据通过导出到 excel 的方式给到业务方核对数据,在模版不固定的情况下,如何快速的导出数据到 excel 显得尤为关键,本文将介绍导出 excel 的方式。
# 2.常见情形
- 报表生成: 在业务应用中,经常需要生成各种形式的报表,以便对数据进行汇总、分析和可视化展示。将数据导出到 Excel 文件可以让用户方便地使用 Excel 等工具进行进一步的数据处理和分析。
- 数据备份: 导出数据到 Excel 文件是一种常见的备份手段。用户可以定期将系统中的关键数据导出到 Excel,以便在需要时进行恢复或迁移。
- 数据交换: 在与其他系统或应用程序进行数据交换时,导出数据到 Excel 是一种通用的方式。Excel 文件格式是广泛支持的,易于在不同系统之间进行数据传递。
- 用户下载: 提供给用户下载其个人或业务数据的功能。这对于在线服务、电子商务平台等应用程序是很常见的需求,用户可以将其数据保存到本地以备查阅。
- 批量操作: 在某些情况下,用户可能需要对大量数据进行批量操作,例如批量更新、删除或进行其他处理。将数据导出到 Excel,用户可以在本地应用程序中更轻松地执行这些操作。
- 报价单、发票等业务文档: 在销售和财务领域,导出 Excel 可以用于生成报价单、发票和其他业务文档,这些文档通常需要以表格形式呈现。
- 数据分享: 有时,用户可能希望分享特定数据的快照或分析结果。将数据导出到 Excel 文件可以方便地与其他人共享数据。
# 二.基础使用
# 1.Apache POI
在 Java 中使用 POI 库(Apache POI)可以方便地操作 Excel 文件,包括导出数据和设置单元格的样式,其中包括背景颜色。下面是一个简单的例子,演示如何在 Java 中使用 POI 库导出带有背景颜色的 Excel 文件。
# 2.pom 依赖
首先,确保你的项目中包含了 Apache POI 库的依赖。如果使用 Maven,可以在 pom.xml
文件中添加以下依赖:
<dependencies>
<!-- Apache POI -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.0.0</version>
</dependency>
</dependencies>
1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
# 3.具体实现
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import java.io.FileOutputStream;
import java.io.IOException;
public class ExcelExporter {
public static void main(String[] args) {
try {
// 创建工作簿
Workbook workbook = new XSSFWorkbook();
// 创建工作表
Sheet sheet = workbook.createSheet("Sheet1");
// 创建样式
CellStyle style = workbook.createCellStyle();
style.setFillForegroundColor(IndexedColors.LIGHT_YELLOW.getIndex());
style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
// 创建行和单元格,并设置背景颜色
Row row = sheet.createRow(0);
Cell cell = row.createCell(0);
cell.setCellValue("内容");
cell.setCellStyle(style);
// 导出文件
try (FileOutputStream fileOut = new FileOutputStream("workbook.xlsx")) {
workbook.write(fileOut);
}
// 关闭工作簿
workbook.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 三.EasyExcel
# 1. EasyExcel
上述使用的原生的 Apache POI 通用性不够强,在某些方面使用起来还是不够方便,接下来将介绍阿里的 EasyExcel 的使用,在 POI 的基础上进行了封装,方便开发者直接使用。
EasyExcel 是阿里巴巴开源的一款基于 Java 的简单、高效的 Excel 文件读写工具。它可以帮助开发者更方便地进行 Excel 文件的读写操作,支持读取大数据量的 Excel 文件并且性能较好。
# 2.相关特性
- 简单易用: EasyExcel 提供了简单的 API,易于上手,使用起来相对轻松。
- 高性能: EasyExcel 使用了基于注解的对象模型,采用零反射、零异常的设计,因此性能较好。在处理大量数据时,相比于一些其他库,EasyExcel 通常表现更出色。
- 支持读写 Excel: 提供了读写 Excel 文件的功能,可以实现从 Excel 文件中读取数据,也可以将数据写入 Excel 文件。
- 支持多种数据模型: 可以支持 Java 普通对象、Map、List 等多种数据模型,便于适应不同的数据结构。
- 支持复杂报表: 可以实现复杂报表的导入导出,包括多表头、合并单元格等。
- 支持自定义样式: 可以自定义 Excel 单元格样式,包括字体、颜色、边框等。
- 支持多种 Excel 格式: 可以读写多种 Excel 格式,包括 xls 和 xlsx。
# 3.简单示例
pom依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.1.2</version>
</dependency>
1
2
3
4
5
2
3
4
5
读取 Excel:
// 读取 Excel 文件
String fileName = "example.xlsx";
EasyExcel.read(fileName, UserData.class, new UserDataListener()).sheet().doRead();
1
2
3
2
3
写入 Excel:
// 写入 Excel 文件
String fileName = "example.xlsx";
List<UserData> data = initData(); // 初始化数据
EasyExcel.write(fileName, UserData.class).sheet("Sheet1").doWrite(data);
1
2
3
4
2
3
4
监听器示例:
public class UserDataListener extends AnalysisEventListener<UserData> {
// 处理每一行的数据
@Override
public void invoke(UserData data, AnalysisContext context) {
System.out.println("Read data: " + data);
}
// 所有数据解析完成后调用
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
System.out.println("All data parsed successfully.");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
这只是一个简单的示例,实际使用中可以根据需求进行更灵活和复杂的配置。EasyExcel 提供了更多的 API 和功能,以满足不同场景下的需求。可以通过 EasyExcel 的官方文档和示例代码深入了解其更多功能和用法:EasyExcel GitHub 仓库 (opens new window)。
# 四.进阶使用
# 1. EasyExcelUtil
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.excel.write.handler.WriteHandler;
import org.apache.poi.ss.formula.functions.T;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class EasyExcelUtil {
/**
* 同步无模型读(默认读取sheet0,从第2行开始读)
*
* @param filePath excel文件的绝对路径
*/
public static List<Map<Integer, String>> syncRead(String filePath) {
return EasyExcelFactory.read(filePath).sheet().doReadSync();
}
/**
* 同步无模型读(自定义读取sheetX,从第2行开始读)
*
* @param filePath excel文件的绝对路径
* @param sheetNo sheet页号,从0开始
*/
public static List<Map<Integer, String>> syncRead(String filePath, Integer sheetNo) {
return EasyExcelFactory.read(filePath).sheet(sheetNo).doReadSync();
}
/**
* 同步无模型读(指定sheet和表头占的行数)
*
* @param filePath
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static List<Map<Integer, String>> syncRead(String filePath, Integer sheetNo, Integer headRowNum) {
return EasyExcelFactory.read(filePath).sheet(sheetNo).headRowNumber(headRowNum).doReadSync();
}
/**
* 同步无模型读(指定sheet和表头占的行数)
*
* @param inputStream
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static List<Map<Integer, String>> syncRead(InputStream inputStream, Integer sheetNo, Integer headRowNum) {
return EasyExcelFactory.read(inputStream).sheet(sheetNo).headRowNumber(headRowNum).doReadSync();
}
/**
* 同步无模型读(指定sheet和表头占的行数)
*
* @param file
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static List<Map<Integer, String>> syncRead(File file, Integer sheetNo, Integer headRowNum) {
return EasyExcelFactory.read(file).sheet(sheetNo).headRowNumber(headRowNum).doReadSync();
}
//====================================================无JAVA模型读取excel数据===============================================================
//====================================================将excel数据同步到JAVA模型属性里===============================================================
/**
* 同步按模型读(默认读取sheet0,从第2行开始读)
*
* @param filePath
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
*/
public static List<T> syncReadModel(String filePath, Class clazz) {
return EasyExcelFactory.read(filePath).sheet().head(clazz).doReadSync();
}
/**
* 同步按模型读(默认表头占一行,从第2行开始读)
*
* @param filePath
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
* @param sheetNo sheet页号,从0开始
*/
public static List<T> syncReadModel(String filePath, Class clazz, Integer sheetNo) {
return EasyExcelFactory.read(filePath).sheet(sheetNo).head(clazz).doReadSync();
}
/**
* 同步按模型读(指定sheet和表头占的行数)
*
* @param inputStream
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static List<T> syncReadModel(InputStream inputStream, Class clazz, Integer sheetNo, Integer headRowNum) {
return EasyExcelFactory.read(inputStream).sheet(sheetNo).headRowNumber(headRowNum).head(clazz).doReadSync();
}
/**
* 同步按模型读(指定sheet和表头占的行数)
*
* @param file
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static List<T> syncReadModel(File file, Class clazz, Integer sheetNo, Integer headRowNum) {
return EasyExcelFactory.read(file).sheet(sheetNo).headRowNumber(headRowNum).head(clazz).doReadSync();
}
/**
* 同步按模型读(指定sheet和表头占的行数)
*
* @param filePath
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static List<T> syncReadModel(String filePath, Class clazz, Integer sheetNo, Integer headRowNum) {
return EasyExcelFactory.read(filePath).sheet(sheetNo).headRowNumber(headRowNum).head(clazz).doReadSync();
}
/**
* 异步无模型读(默认读取sheet0,从第2行开始读)
*
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param filePath 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static void asyncRead(String filePath, AnalysisEventListener<T> excelListener) {
EasyExcelFactory.read(filePath, excelListener).sheet().doRead();
}
/**
* 异步无模型读(默认表头占一行,从第2行开始读)
*
* @param filePath 表头占的行数,从0开始(如果要连表头一起读出来则传0)
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param sheetNo sheet页号,从0开始
*/
public static void asyncRead(String filePath, AnalysisEventListener<T> excelListener, Integer sheetNo) {
EasyExcelFactory.read(filePath, excelListener).sheet(sheetNo).doRead();
}
/**
* 异步无模型读(指定sheet和表头占的行数)
*
* @param inputStream
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static void asyncRead(InputStream inputStream, AnalysisEventListener<T> excelListener, Integer sheetNo, Integer headRowNum) {
EasyExcelFactory.read(inputStream, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();
}
/**
* 异步无模型读(指定sheet和表头占的行数)
*
* @param file
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static void asyncRead(File file, AnalysisEventListener<T> excelListener, Integer sheetNo, Integer headRowNum) {
EasyExcelFactory.read(file, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();
}
/**
* 异步无模型读(指定sheet和表头占的行数)
*
* @param filePath
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
* @return
*/
public static void asyncRead(String filePath, AnalysisEventListener<T> excelListener, Integer sheetNo, Integer headRowNum) {
EasyExcelFactory.read(filePath, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();
}
/**
* 异步按模型读取(默认读取sheet0,从第2行开始读)
*
* @param filePath
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
*/
public static void asyncReadModel(String filePath, AnalysisEventListener<T> excelListener, Class clazz) {
EasyExcelFactory.read(filePath, clazz, excelListener).sheet().doRead();
}
/**
* 异步按模型读取(默认表头占一行,从第2行开始读)
*
* @param filePath
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
* @param sheetNo sheet页号,从0开始
*/
public static void asyncReadModel(String filePath, AnalysisEventListener<T> excelListener, Class clazz, Integer sheetNo) {
EasyExcelFactory.read(filePath, clazz, excelListener).sheet(sheetNo).doRead();
}
/**
* 异步按模型读取
*
* @param inputStream
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static void asyncReadModel(InputStream inputStream, AnalysisEventListener<T> excelListener, Class clazz, Integer sheetNo, Integer headRowNum) {
EasyExcelFactory.read(inputStream, clazz, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();
}
/**
* 异步按模型读取
*
* @param file
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static void asyncReadModel(File file, AnalysisEventListener<T> excelListener, Class clazz, Integer sheetNo, Integer headRowNum) {
EasyExcelFactory.read(file, clazz, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();
}
/**
* 异步按模型读取
*
* @param filePath
* @param excelListener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
* @param sheetNo sheet页号,从0开始
* @param headRowNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
*/
public static void asyncReadModel(String filePath, AnalysisEventListener<T> excelListener, Class clazz, Integer sheetNo, Integer headRowNum) {
EasyExcelFactory.read(filePath, clazz, excelListener).sheet(sheetNo).headRowNumber(headRowNum).doRead();
}
/**
* 无模板写文件
*
* @param filePath
* @param head 表头数据
* @param data 表内容数据
*/
public static void write(String filePath, List<List<String>> head, List<List<Object>> data) {
EasyExcel.write(filePath).head(head).sheet().doWrite(data);
}
/**
* 无模板写文件
*
* @param filePath
* @param head 表头数据
* @param data 表内容数据
* @param sheetNo sheet页号,从0开始
* @param sheetName sheet名称
*/
public static void write(String filePath, List<List<String>> head, List<List<Object>> data, Integer sheetNo, String sheetName) {
EasyExcel.write(filePath).head(head).sheet(sheetNo, sheetName).doWrite(data);
}
/**
* 根据excel模板文件写入文件
*
* @param filePath
* @param templateFileName
* @param headClazz
* @param data
*/
public static void writeTemplate(String filePath, String templateFileName, Class headClazz, List data) {
EasyExcel.write(filePath, headClazz).withTemplate(templateFileName).sheet().doWrite(data);
}
/**
* 根据excel模板文件写入文件
*
* @param filePath
* @param templateFileName
* @param data
*/
public static void writeTemplate(String filePath, String templateFileName, List data) {
EasyExcel.write(filePath).withTemplate(templateFileName).sheet().doWrite(data);
}
/**
* 按模板写文件
*
* @param filePath
* @param headClazz 表头模板
* @param data 数据
*/
public static void write(String filePath, Class headClazz, List data) {
EasyExcel.write(filePath, headClazz).sheet().doWrite(data);
}
/**
* 按模板写文件
*
* @param filePath
* @param headClazz 表头模板
* @param data 数据
* @param sheetNo sheet页号,从0开始
* @param sheetName sheet名称
*/
public static void write(String filePath, Class headClazz, List data, Integer sheetNo, String sheetName) {
EasyExcel.write(filePath, headClazz).sheet(sheetNo, sheetName).doWrite(data);
}
/**
* 按模板写文件
*
* @param filePath
* @param headClazz 表头模板
* @param data 数据
* @param writeHandler 自定义的处理器,比如设置table样式,设置超链接、单元格下拉框等等功能都可以通过这个实现(需要注册多个则自己通过链式去调用)
* @param sheetNo sheet页号,从0开始
* @param sheetName sheet名称
*/
public static void write(String filePath, Class headClazz, List data, WriteHandler writeHandler, Integer sheetNo, String sheetName) {
EasyExcel.write(filePath, headClazz).registerWriteHandler(writeHandler).sheet(sheetNo, sheetName).doWrite(data);
}
/**
* 按模板写文件(包含某些字段)
*
* @param filePath
* @param headClazz 表头模板
* @param data 数据
* @param includeCols 包含字段集合,根据字段名称显示
* @param sheetNo sheet页号,从0开始
* @param sheetName sheet名称
*/
public static void writeInclude(String filePath, Class headClazz, List data, Set<String> includeCols, Integer sheetNo, String sheetName) {
EasyExcel.write(filePath, headClazz).includeColumnFiledNames(includeCols).sheet(sheetNo, sheetName).doWrite(data);
}
/**
* 按模板写文件(排除某些字段)
*
* @param filePath
* @param headClazz 表头模板
* @param data 数据
* @param excludeCols 过滤排除的字段,根据字段名称过滤
* @param sheetNo sheet页号,从0开始
* @param sheetName sheet名称
*/
public static void writeExclude(String filePath, Class headClazz, List data, Set<String> excludeCols, Integer sheetNo, String sheetName) {
EasyExcel.write(filePath, headClazz).excludeColumnFiledNames(excludeCols).sheet(sheetNo, sheetName).doWrite(data);
}
/**
* 多个sheet页的数据链式写入
* ExcelUtil.writeWithSheets(outputStream)
* .writeModel(ExcelModel.class, excelModelList, "sheetName1")
* .write(headData, data,"sheetName2")
* .finish();
*
* @param outputStream
*/
public static EasyExcelWriterFactory writeWithSheets(OutputStream outputStream) {
EasyExcelWriterFactory excelWriter = new EasyExcelWriterFactory(outputStream);
return excelWriter;
}
/**
* 多个sheet页的数据链式写入
* ExcelUtil.writeWithSheets(file)
* .writeModel(ExcelModel.class, excelModelList, "sheetName1")
* .write(headData, data,"sheetName2")
* .finish();
*
* @param file
*/
public static EasyExcelWriterFactory writeWithSheets(File file) {
EasyExcelWriterFactory excelWriter = new EasyExcelWriterFactory(file);
return excelWriter;
}
/**
* 多个sheet页的数据链式写入
* ExcelUtil.writeWithSheets(filePath)
* .writeModel(ExcelModel.class, excelModelList, "sheetName1")
* .write(headData, data,"sheetName2")
* .finish();
*
* @param filePath
*/
public static EasyExcelWriterFactory writeWithSheets(String filePath) {
EasyExcelWriterFactory excelWriter = new EasyExcelWriterFactory(filePath);
return excelWriter;
}
/**
* 多个sheet页的数据链式写入(失败了会返回一个有部分数据的Excel)
* ExcelUtil.writeWithSheets(response, exportFileName)
* .writeModel(ExcelModel.class, excelModelList, "sheetName1")
* .write(headData, data,"sheetName2")
* .finish();
*
* @param response
* @param exportFileName 导出的文件名称
*/
public static EasyExcelWriterFactory writeWithSheetsWeb(HttpServletResponse response, String exportFileName) throws IOException {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码
String fileName = URLEncoder.encode(exportFileName, "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
EasyExcelWriterFactory excelWriter = new EasyExcelWriterFactory(response.getOutputStream());
return excelWriter;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
# 2.EasyExcelWriterFactory
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import java.io.File;
import java.io.OutputStream;
import java.util.List;
public class EasyExcelWriterFactory {
private int sheetNo = 0;
private ExcelWriter excelWriter = null;
public EasyExcelWriterFactory(OutputStream outputStream) {
excelWriter = EasyExcel.write(outputStream).build();
}
public EasyExcelWriterFactory(File file) {
excelWriter = EasyExcel.write(file).build();
}
public EasyExcelWriterFactory(String filePath) {
excelWriter = EasyExcel.write(filePath).build();
}
/**
* 链式模板表头写入
*
* @param headClazz 表头格式
* @param data 数据 List<ExcelModel> 或者List<List<Object>>
* @return
*/
public EasyExcelWriterFactory writeModel(Class headClazz, List data, String sheetName) {
excelWriter.write(data, EasyExcel.writerSheet(this.sheetNo++, sheetName).head(headClazz).build());
return this;
}
/**
* 链式自定义表头写入
*
* @param head
* @param data 数据 List<ExcelModel> 或者List<List<Object>>
* @param sheetName
* @return
*/
public EasyExcelWriterFactory write(List<List<String>> head, List data, String sheetName) {
excelWriter.write(data, EasyExcel.writerSheet(this.sheetNo++, sheetName).head(head).build());
return this;
}
/**
* 使用此类结束后,一定要关闭流
*/
public void finish() {
excelWriter.finish();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# 3.ExcelUtil
import lombok.extern.slf4j.Slf4j;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
/**
* @className com.yq.common.utils.ExcelUtil
* @author: yangjie
* @description: ExcelUtil
*/
@Slf4j
public class ExcelUtil {
/**
* 下载Excel
*
* @param response 请求response
* @param fileName 下载文件名称 xxx.xlsx
* @param filePath 下载文件路径 D://xxx/xxx
*/
public static void downExcel(HttpServletResponse response, String fileName, String filePath) {
// path是指想要下载的文件的路径
File file = new File(filePath);
ExcelUtil.downExcel(response, fileName, file);
}
/**
* 下载Excel
*
* @param response 请求response
* @param fileName 下载文件名称 xxx.xlsx
* @param file 下载文件流
*/
public static void downExcel(HttpServletResponse response, String fileName, File file) {
FileInputStream fileInputStream = null;
InputStream fis = null;
OutputStream outputStream = null;
try {
// 将文件写入输入流
fileInputStream = new FileInputStream(file);
fis = new BufferedInputStream(fileInputStream);
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
// 清空response
response.reset();
// 设置response的Header
// 解决跨域问题,这句话是关键,对任意的域都可以,如果需要安全,可以设置成安前的域名
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
response.setHeader("FileName", URLEncoder.encode(fileName, "UTF-8"));
response.setHeader("Access-Control-Expose-Headers", "FileName");
response.setCharacterEncoding("UTF-8");
//Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存
//attachment表示以附件方式下载 inline表示在线打开 "Content-Disposition: inline; filename=文件名.mp3"
// filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
// 告知浏览器文件的大小
response.addHeader("Content-Length", "" + file.length());
outputStream = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
outputStream.write(buffer);
outputStream.flush();
} catch (IOException e) {
e.printStackTrace();
log.error("文件下载异常,{}", e);
} finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# 4.使用方式
实体类:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class AiAssistantForecastVo implements Serializable {
private static final long serialVersionUID = -2986024804753822180L;
@ExcelProperty(value = "细分品类", index = 0)
private String presentName;
@ExcelProperty(value = "大区", index = 1)
private String regionNo;
@ExcelProperty(value = "管理城市", index = 2)
private String managingCityNo;
@ExcelProperty(value = "店铺", index = 3)
private String organKey;
@ExcelProperty(value = "指标", index = 4)
private String indicator;
@ExcelProperty(value = "202336", index = 5)
private String naturalYearWeek202336;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
调用:
String fileName = "xxxx_" + System.currentTimeMillis() + ".xlsx";
String tempPath = "/home/uploads/";
String filePath = tempPath + fileName;
EasyExcelWriterFactory res = EasyExcelUtil.writeWithSheets(filePath)
.writeModel(AiAssistantForecastVo.class, aiAssistantForecastVoList, "报表数据");
res.finish();
ExcelUtil.downExcel(response, fileName, filePath);
1
2
3
4
5
6
7
2
3
4
5
6
7
# 5.如何处理不确定的字段?
- 通过反射拿到字段。
- 拿到字段后可以拿字段的注解。
- 根据字段注解的属性值可以确定需要填充的是哪个动态的字段。
- 通过使用 Reflect 反射工具类,很好的动态的填充了属性值。
//判断,周数相等的时候,才能塞进去
final Integer naturalYear = forecast.getNaturalYear();
final Integer naturalYearWeek = forecast.getNaturalYearWeek();
// 获取类的所有字段
Field[] fields = aiAssistantForecastVo.getClass().getDeclaredFields();
// 遍历字段
for (Field field : fields) {
field.setAccessible(true);
// 判断字段上是否有指定的注解
if (field.isAnnotationPresent(ExcelProperty.class)) {
// 获取字段上的注解
ExcelProperty annotation = field.getAnnotation(ExcelProperty.class);
// 获取注解的属性值
final String[] value = annotation.value();
final String property = value[0];
if (StringUtils.equals(property, naturalYear.toString() + naturalYearWeek.toString())) {
try {
field.set(aiAssistantForecastVo, Objects.nonNull(Reflect.on(forecast).field(mapValue).get()) ? Reflect.on(forecast).field(mapValue).get().toString() : "");
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 6.需要注意的点
- 如果是 docker 部署,记得设置挂载地址/home/uploads/
#docker run的时候设置挂载地址
-v /home/uploads:/home/uploads
1
2
2
- 下载时间过长,可能需要设置 nginx 的超时时间
http {
proxy_connect_timeout 300; #单位秒
proxy_send_timeout 300; #单位秒
proxy_read_timeout 300; #单位秒
proxy_buffer_size 16k;
proxy_buffers 4 64k;
proxy_busy_buffers_size 128k;
proxy_temp_file_write_size 128k;
}
1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10