# 1. 模板下載
模板

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}創建,創建后使用樣式

設置合并和邊框后

## 方法二,假填充數據
上邊的方法合并單元格會出現,在office中有此問題(合并單元格有問題提)

解決方法:
**假數據單元格,會被創建,樣式會起作用**,注意模板要干凈,不要有過樣式修改,否則在工具處理后出現多創建單元格的現象,此時清空模板多余部分的內容(即時沒有內容)
代碼如下:
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
- 規范
- 數據庫