# 1. 模板下载
模板
![](https://img.kancloud.cn/4f/51/4f51e4109c87717b7b7337693e37c2cc_1418x774.png)
excel,rowIndex和columnIndex从0开始,所以list列表从第19行开始,属于新建,会传入CellWriteHandler 中
> CellWriteHandler
* beforeCellCreate:创建一个cell前(模板上没有的行-从19行开始)调用,本实例在这个方法中创建cell,并指定样式。其他两个方法没有试过
* afterCellDispose:数据填充后调用,用于合并单元格
## 1.1 controller
~~~java
@SneakyThrows
@GetMapping("/export-task/{id}")
@ApiOperation("下载任务单:/opsTestTask/export-task/{id}")
public void exportPlan(HttpServletResponse response, @PathVariable String id) {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding(Charsets.UTF_8.name());
// File file = ResourceUtils.getFile("classpath:tpl" + File.separator + "task_detail_tpl.xlsx");
// ClassPathResource classPathResource = new ClassPathResource("tpl"+ File.separator + "task_detail_tpl.xlsx");
// File file = classPathResource.getFile();
InputStream inputStream = TestTaskController.class.getClassLoader().getResourceAsStream("tpl/task_detail_tpl.xlsx");
String tmpFileName = UUID.randomUUID().toString()+".xlsx";
File file = new File(tmpFileName);
FileUtils.copyInputStreamToFile(inputStream,file);
TaskExportVo taskExportVo = testTaskService.getTaskExportVoById(id);
String fileName = URLEncoder.encode("任务单_"+taskExportVo.getTaskCode(), Charsets.UTF_8.name());
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(file.getAbsolutePath()).registerWriteHandler(new TaskExcelFillCellStrategy(taskExportVo.getTaskExportListVo().size())).build();
WriteSheet writeSheet = EasyExcel.writerSheet().build();
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
excelWriter.fill(taskExportVo.getTaskExportListVo(), fillConfig, writeSheet);
excelWriter.fill(taskExportVo, writeSheet);
excelWriter.finish();
Files.delete(Paths.get(file.getAbsolutePath()));
}
~~~
## 1.2 填充数据设置
~~~
package com.faw_qm.ad_ops.close_test.config.excel;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import sun.awt.HKSCS;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* @author WongChy
* 2021/3/29
*/
@Slf4j
@NoArgsConstructor
@Data
@RequiredArgsConstructor
public class TaskExcelFillCellStrategy implements CellWriteHandler {
/**
* 开始的行数
* 从这一行开始才进行列合并操作
*/
private int beginRow = 17;
@NonNull
private int endRowLength;
private static List<Integer> columnStartIndex = Arrays.asList(1, 4);
@Override
public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
setFilledExcellStyle(writeSheetHolder,row);
}
@Override
public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
}
@Override
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
if (isHead) {
return;
}
this.setCellStyle(cell);
merge(writeSheetHolder.getSheet(), cell, head, relativeRowIndex);
}
void setCellStyle(Cell cell){
CellStyle cellStyle = cell.getCellStyle();
cellStyle.setBorderLeft(BorderStyle.THIN);
cellStyle.setBorderRight(BorderStyle.THIN);
cellStyle.setBorderBottom(BorderStyle.THIN);
cellStyle.setBorderTop(BorderStyle.THIN);
cell.setCellStyle(cellStyle);
}
//list需要合并单元格
void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex){
if (relativeRowIndex == null) {
return;
}
int columnIndex = cell.getColumnIndex();
int rowIndex = cell.getRowIndex();
if (columnStartIndex.contains(columnIndex) && rowIndex >= beginRow) {
sheet.getWorkbook().createCellStyle();
CellRangeAddress cellRangeAddress = new CellRangeAddress(
cell.getRowIndex(),
cell.getRowIndex(),
cell.getColumnIndex(),
cell.getColumnIndex() + 2
);
sheet.addMergedRegionUnsafe(cellRangeAddress);
}
}
//设置单个cell样式,避免合并后新填充的单元格没有边框
public void setFilledExcellStyle(WriteSheetHolder writeSheetHolder, Row row) {
CellStyle cellStyle = writeSheetHolder.getSheet().getWorkbook().createCellStyle();
cellStyle.setBorderLeft(BorderStyle.THIN);
cellStyle.setBorderRight(BorderStyle.THIN);
cellStyle.setBorderBottom(BorderStyle.THIN);
cellStyle.setBorderTop(BorderStyle.THIN);
Iterator<Integer> iterator = columnStartIndex.iterator();
while (iterator.hasNext()) {
Integer columnIndex = iterator.next();
System.out.println("aa:" + row.getRowNum() + ":" + columnIndex);
Cell cell1 = row.createCell(columnIndex + 1);
Cell cell2 = row.createCell(columnIndex + 2);
cell1.setCellStyle(cellStyle);
cell2.setCellStyle(cellStyle);
}
}
}
~~~
## 1.3 数据
~~~
package com.faw_qm.ad_ops.close_test.vo;
import lombok.Data;
import java.util.Date;
import java.util.List;
/**
* @author WongChy
* 2021/3/29
*/
@Data
public class TaskExportVo {
private String planCode;
private String taskCode;
private String subjectName;
private String subjectAddress;
private String vehicleBrand;
private String modelName;
private String vehicleVin;
private String engineNum;
private Date planStartTime;
private Date planEndTime;
private Date actStartTime;
private Date actEndTime;
private String contactPerson;
private String contactPhone;
private String regionName;
private String testTypeStr;
private String videoMonitorListStr;
private String obuEquListStr;
private String auxEquListStr;
private String siteListStr;
private List<TaskExportListVo> taskExportListVo;
}
~~~
问题,是因为工具创建单元格是根据{data}创建,创建后使用样式
![](https://img.kancloud.cn/94/f7/94f794aae7841adf9689b2b70481c69e_1396x683.png)
设置合并和边框后
![](https://img.kancloud.cn/a8/a2/a8a29fc6a18b7a377d2bea8d4ec5ea49_1372x574.png)
## 方法二,假填充数据
上边的方法合并单元格会出现,在office中有此问题(合并单元格有问题提)
![](https://img.kancloud.cn/b8/6d/b86d6170f6de7cd17dc3d59b2ed1bc30_1118x515.png)
解决方法:
**假数据单元格,会被创建,样式会起作用**,注意模板要干净,不要有过样式修改,否则在工具处理后出现多创建单元格的现象,此时清空模板多余部分的内容(即时没有内容)
代码如下:
1. controller
~~~
@GetMapping("/export-task/{id}")
@ApiOperation("下载任务单:/opsTestTask/export-task/{id}")
public void exportPlan(HttpServletResponse response, @PathVariable String id) {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding(Charsets.UTF_8.name());
// File file = ResourceUtils.getFile("classpath:tpl" + File.separator + "task_detail_tpl.xlsx");
// ClassPathResource classPathResource = new ClassPathResource("tpl"+ File.separator + "task_detail_tpl.xlsx");
// File file = classPathResource.getFile();
InputStream inputStream = TestTaskController.class.getClassLoader().getResourceAsStream("tpl/task_detail_tpl.xlsx");
String tmpFileName = UUID.randomUUID().toString()+".xlsx";
File file = new File(tmpFileName);
FileUtils.copyInputStreamToFile(inputStream,file);
TaskExportVo taskExportVo = testTaskService.getTaskExportVoById(id);
String fileName = URLEncoder.encode("任务单_"+taskExportVo.getTaskCode(), Charsets.UTF_8.name());
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).withTemplate(file.getAbsolutePath()).registerWriteHandler(new TaskExcelFillCellStrategy(taskExportVo.getTaskExportListVo().size())).build();
WriteSheet writeSheet = EasyExcel.writerSheet().build();
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
excelWriter.fill(taskExportVo.getTaskExportListVo(), fillConfig, writeSheet);
excelWriter.fill(taskExportVo, writeSheet);
excelWriter.finish();
Files.delete(Paths.get(file.getAbsolutePath()));
}
~~~
2. vo
~~~
@Data
public class TaskExportVo {
private String planCode;
private String taskCode;
private String subjectName;
private String subjectAddress;
private String vehicleBrand;
private String modelName;
private String vehicleVin;
private String engineNum;
private Date planStartTime;
private Date planEndTime;
private Date actStartTime;
private Date actEndTime;
private String contactPerson;
private String contactPhone;
private String regionName;
private String testTypeStr;
private String videoMonitorListStr;
private String obuEquListStr;
private String auxEquListStr;
private String siteListStr;
private String targetVehicle;
private List<TaskExportListVo> taskExportListVo;
}
@Data
public class TaskExportListVo {
private Integer sceneOrder;
private String detectionItemName;
private String sceneName;
private String voidCellOne; //填充假数据(数据为null),工具会创建单元格,方便统一设置样式
private String voidCellTwo; //填充假数据
private String voidCellThree;//填充假数据
private String voidCellFour;//填充假数据
}
~~~
3. 单元格处理策略
~~~
package com.faw_qm.ad_ops.close_test.config.excel;
import com.alibaba.excel.metadata.CellData;
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import sun.awt.HKSCS;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* @author WongChy
* 2021/3/29
*/
@Slf4j
@NoArgsConstructor
@Data
@RequiredArgsConstructor
public class TaskExcelFillCellStrategy implements CellWriteHandler {
/**
* 开始的行数
* 从这一行开始才进行列合并操作
*/
private int beginRow = 17;
@NonNull
private int endRowLength;
private static List<Integer> columnStartIndex = Arrays.asList(1, 4);
@Override
public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {
}
@Override
public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
}
@Override
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<CellData> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
if (isHead) {
return;
}
this.setCellStyle(cell);
merge(writeSheetHolder.getSheet(), cell, head, relativeRowIndex);
}
void setCellStyle(Cell cell){
CellStyle cellStyle = cell.getCellStyle();
cellStyle.setBorderLeft(BorderStyle.THIN);
cellStyle.setBorderRight(BorderStyle.THIN);
cellStyle.setBorderBottom(BorderStyle.THIN);
cellStyle.setBorderTop(BorderStyle.THIN);
cell.setCellStyle(cellStyle);
}
void merge(Sheet sheet, Cell cell, Head head, Integer relativeRowIndex){
if (relativeRowIndex == null) {
return;
}
int columnIndex = cell.getColumnIndex();
int rowIndex = cell.getRowIndex();
if (columnStartIndex.contains(columnIndex) && rowIndex >= beginRow) {
sheet.getWorkbook().createCellStyle();
CellRangeAddress cellRangeAddress = new CellRangeAddress(
cell.getRowIndex(),
cell.getRowIndex(),
cell.getColumnIndex(),
cell.getColumnIndex() + 2
);
sheet.addMergedRegionUnsafe(cellRangeAddress);
}
}
}
~~~
下载的文件正确合并单元格,且不会有错误提示
- 计算机网络
- 基础_01
- tcp/ip
- http转https
- Let's Encrypt免费ssl证书(基于haproxy负载)
- what's the http?
- 网关
- 网络IO
- http
- 工具
- Git
- 初始本地仓库并上传
- git保存密码
- Gitflow
- maven
- 1.生命周期命令
- 聚合与继承
- 插件管理
- assembly
- 资源管理插件
- 依赖范围
- 分环境打包
- dependencyManagement
- 版本分类
- 找不到主类
- 无法加载主类
- 私服
- svn
- gradle
- 手动引入第三方jar包
- 打包exe文件
- Windows
- java
- 设计模式
- 七大原则
- 1.开闭原则
- 2. 里式替换原则
- 3. 依赖倒置原则
- 4. 单一职责原则
- 单例模式
- 工厂模式
- 简单工厂
- 工厂方法模式
- 抽象工厂模式
- 观察者模式
- 适配器模式
- 建造者模式
- 代理模式
- 适配器模式
- 命令模式
- json
- jackson
- poi
- excel
- easy-poi
- 规则
- 模板
- 合并单元格
- word
- 读取
- java基础
- 类路径与jar
- 访问控制权限
- 类加载
- 注解
- 异常处理
- String不可变
- 跨域
- transient关键字
- 二进制编码
- 泛型1
- 与或非
- final详解
- Java -jar
- 正则
- 读取jar
- map
- map计算
- hashcode计算原理
- 枚举
- 序列化
- URLClassLoader
- 环境变量和系统变量
- java高级
- java8
- 1.Lambda表达式和函数式接口
- 2.接口的默认方法和静态方法
- 3.方法引用
- 4.重复注解
- 5.类型推断
- 6.拓宽注解的应用场景
- java7-自动关闭资源机制
- 泛型
- stream
- 时区的正确理解
- StringJoiner字符串拼接
- 注解
- @RequestParam和@RequestBody的区别
- 多线程
- 概念
- 线程实现方法
- 守护线程
- 线程阻塞
- 笔试题
- 类加载
- FutureTask和Future
- 线程池
- 同步与异步
- 高效简洁的代码
- IO
- ThreadLocal
- IO
- NIO
- 图片操作
- KeyTool生成证书
- 压缩图片
- restful
- 分布式session
- app保持session
- ClassLoader.getResources 能搜索到的资源路径
- java开发规范
- jvm
- 高并发
- netty
- 多线程与多路复用
- 异步与事件驱动
- 五种IO模型
- copy on write
- code style
- 布隆过滤器
- 笔试
- 数据库
- mybatis
- mybatis与springboot整合配置
- pagehelper
- 分页数据重复问题
- Java与数据库之间映射
- 拦截器
- 拦截器应用
- jvm
- 堆内存测试
- 线程栈
- 直接内存
- 内存结构
- 内存模型
- 垃圾回收
- 调优
- 符号引用
- 运行参数
- 方法区
- 分带回收理论
- 快捷开发
- idea插件
- 注释模板
- git
- pull冲突
- push冲突
- Excel处理
- 图片处理
- 合并单元格
- easypoi
- 模板处理
- 响应式编程
- reactor
- reactor基础
- jingyan
- 规范
- 数据库