EasyExcel:
大家好久不见!
一、什么是EasyExcel?
EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。
github地址: https://github.com/alibaba/easyexcel
官方文档地址:https://www.yuque.com/easyexcel/doc/easyexcel
当然还有一个POI也可以实现操作Excel,Apache POI提供API给Java程序对Microsoft Office格式档案读和写的功能,POI为“Poor Obfuscation Implementation”的首字母缩写,意为“简洁版的模糊实现”
EasyExcel和POI的区别:
二、EasyExcel的实际使用
首先导入依赖:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.4</version>
</dependency>
依赖版本按需使用即可,一些基本的操作大家看官方文档即可,在这里就记录一下自己通过EasyExcel导出的表格及过程
首先看一下需要导出的模板:
区域说明:
A:单个单元格填充
B:列表填充
C:列表填充(但单元格格式与B区域不同)
D:单个单元格填充
一、A区域的填充数据模板的设置:
这个区域填充还是很简单的,只要给每个表格取个参数名,用{}包裹,Java里面用Map来填充即可。Map的键对应{}里面的参数名,对应的值就是填充进去的值:
例如这样,以下数据都是从数据库查出后填入Map即可:
HashMap<String, Object> workOrderFileData = new HashMap<>();
for (ProjectWorkOrderFileData p : WorkOrderFileList) {
workOrderFileData.put("projectCode", p.getProjectCode());
workOrderFileData.put("projectName", p.getProjectName());
workOrderFileData.put("inspectedEnt", p.getInspectedEnt());
workOrderFileData.put("customerAddress", p.getCustomerAddress());
workOrderFileData.put("linkMan", p.getLinkMan());
workOrderFileData.put("linkPhone", p.getLinkPhone());
workOrderFileData.put("bizCreateTime", p.getBizCreateTime().substring(0, 10));
workOrderFileData.put("customerName", p.getCustomerName());
workOrderFileData.put("prodCompany", p.getProdCompany());
workOrderFileData.put("testCode", p.getTestCode());
workOrderFileData.put("batchCode", p.getBatchCode());
workOrderFileData.put("region", region);
}
二、B和C区域的填充数据模板的设置:
因为B区域是有两个列表,所以我们一定在模板内区分开来,给每个列表取个参数名,然后给每个字段取个参数名,通过列表参数名点(.)字段参数名即可:
即:{} 代表普通变量, {.} 代表是list的变量 {前缀.} 前缀可以区分不同的list
注意: 用{} 来表示你要用的变量 如果本来就有"{“,”}" 特殊字符 用"{“,”}"代替
对于这两组数据,Java里面使用两个列表储存即可,列表内可以存一个对象:
ArrayList<SampleData> SampleDataList = service.selectSampleData(proId);
ArrayList<AnalysisMethodData> analysisMethodDataList = service.selectAnalysisMethodData(proId);
对象里面的字段名即是该列表所有字段名:
@NoArgsConstructor
@AllArgsConstructor
@Data
@Builder
public class SampleData {
private int number;
private String typeName;
private String redFolderName;
private String redAnalyzeItems;
private int spotFrequency;
private int frequency;
private int day;
private int cycleOrder;
private int timesOrder;
private int sampleOrder;
private String remarks;
}
D区域的填充和A区域是一样的,通过Map来填充即可,因为是一对一的关系
三、两个列表之间单元格样式不一样的解决办法
通过模板可以看到BC两个列表之前的单元格样式不一样,如果不做处理,在填充的时候,C区域的样式会根据B区域的样式来填充,也就是C区域合并的单元格会被拆分,所以需要一个工具类:
public class MyHandler extends AbstractMergeStrategy {
@Override
protected void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex) {
if (relativeRowIndex == null || relativeRowIndex == 0) {
return;
}
int rowIndex = cell.getRowIndex();
int colIndex = cell.getColumnIndex();
sheet = cell.getSheet();
Row preRow = sheet.getRow(rowIndex - 1);
Cell preCell = preRow.getCell(colIndex);//获取上一行的该格
List<CellRangeAddress> list = sheet.getMergedRegions();
CellStyle cs = cell.getCellStyle();
cell.setCellStyle(cs);
for (CellRangeAddress cellRangeAddress : list) {
if (cellRangeAddress.containsRow(preCell.getRowIndex()) && cellRangeAddress.containsColumn(preCell.getColumnIndex())) {
int lastColIndex = cellRangeAddress.getLastColumn();
int firstColIndex = cellRangeAddress.getFirstColumn();
CellRangeAddress cra = new CellRangeAddress(cell.getRowIndex(), cell.getRowIndex(), firstColIndex, lastColIndex);
sheet.addMergedRegion(cra);
RegionUtil.setBorderBottom(BorderStyle.THIN, cra, sheet);
RegionUtil.setBorderLeft(BorderStyle.THIN, cra, sheet);
RegionUtil.setBorderRight(BorderStyle.THIN, cra, sheet);
RegionUtil.setBorderTop(BorderStyle.THIN, cra, sheet);
return;
}
}
}
}
直接复制当工具类就完事!
三、完成其他配置,开始填充
我们还要设置两个地址:模板的位置以及导出后文件的储存位置,以下是控制层完整代码
@GetMapping(path = "/generateProjectWorkOrder")
public JsonResp<String> generateProjectWorkOrder(@RequestParam("proId") String proId) {
//设置模板位置以及导出后储存位置
String templateFile = "D:/WorkProject/ExcelTemplate/模板.xls";
String resultFile = "D:/WorkProject/ExcelTemplate/WorkOrderFile/" +"导出结果.xls";
FillConfig fillConfig = FillConfig.builder().forceNewRow(true).build();
ExcelWriter excelWriter = EasyExcel.write(resultFile).withTemplate(templateFile).build();
//使用让该组单元格格式不受影响的工具类
WriteSheet sheet = EasyExcel.writerSheet(0).registerWriteHandler(new MyHandler()).build();
//获取数据(分别是ABCD四组数据)
HashMap<String, Object> workOrderFileData = service.selectWorkOrder(proId);
ArrayList<SampleData> SampleDataList = service.selectSampleData(proId);
ArrayList<AnalysisMethodData> analysisMethodDataList = service.selectAnalysisMethodData(proId);
HashMap<String, String> reviewerData = service.selectReviewerData(proId);
//单组填充(A区域)
excelWriter.fill(workOrderFileData, sheet);
//多个列表填充(B、C区域)
// 如果有多个list 模板上必须有{前缀.} 这里的前缀就是 data1,然后多个list必须用 FillWrapper包裹
excelWriter.fill(new FillWrapper("data1", SampleDataList), fillConfig, sheet);
excelWriter.fill(new FillWrapper("data2", analysisMethodDataList), fillConfig, sheet);
//单组填充(D区域)
excelWriter.fill(reviewerData, sheet);
//关闭流
excelWriter.finish();
//返回数据
JsonResp<String> objectJsonResp = new JsonResp<>();
//返回路径
resultFile = "lims/downloadGenerateExcel?path=" + resultFile;
objectJsonResp.setData(resultFile);
return objectJsonResp;
}
数据获取的方式按照自己的格式来即可,若完成以上编写,可以实现本地的导出与下载,若需要部署在服务器上,则还需要从服务器下载到本地
三、服务器项目导出后下载到本地
若把项目部署在服务器上,使用该功能首先是下载到服务器上,然后再从服务器上下载到本地电脑。
我们可以使用HttpServletResponse response 来操作:
代码如下:
@GetMapping(path = "/downloadGenerateExcel")
public void download(String path, HttpServletResponse response) {
try {
// path是指想要下载的文件的路径
File file = new File(path);
String filename = file.getName();
// 将文件写入输入流
FileInputStream fileInputStream = new FileInputStream(file);
InputStream fis = new BufferedInputStream(fileInputStream);
byte[] buffer = new byte[fis.available()];
//noinspection ResultOfMethodCallIgnored
fis.read(buffer);
fis.close();
response.reset();
response.setCharacterEncoding("UTF-8");
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
// 告知浏览器文件的大小
response.addHeader("Content-Length", "" + file.length());
OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
outputStream.write(buffer);
outputStream.flush();
} catch (IOException ex) {
ex.printStackTrace();
}
}
这个方法的参数就是上述方法最后的代码:
//返回数据
JsonResp<String> objectJsonResp = new JsonResp<>();
//返回路径
resultFile = "lims/downloadGenerateExcel?path=" + resultFile;
objectJsonResp.setData(resultFile);
return objectJsonResp;
也就是导出完成后返回的参数可以自动调用这个方法,把返回的参数自动传入该方法的Path,即可完成从服务器下载到本地。
四、使用效果
运行对应的接口,即可把数据导出并填充在指定的Excel模板内,并下载到指定位置,使用以上模板导出的结果如下:
以上均为模拟数据,大家根据实际情况来编写即可
在最后附上EasyExcel最新的文档地址:https://easyexcel.opensource.alibaba.com/docs/current/quickstart/fill