參考:https://blog.csdn.net/hanyi_/article/details/118117484,https://blog.csdn.net/sunyuhua_keyboard/article/details/125482353,https://lhalcyon.com/alpine-font-issue/,https://github.com/alibaba/easyexcel/issues/1476(沒字型)
首先吧,因為每週列會需要看使用者使用情況報表,我這邊需要把整合好的資料整理下給產品,但我比較追求便捷化,想一次性做成並匯出成excel,上回直接拿匯出的sql已經夠難看了,這回想做個全面自動化,接著就是行動,需求是看到使用者使用的多個裝置的不同使用時長情況,這個其實很簡單無非就是把使用者跟裝置一條條全查出來,挨的近些到時候直接合並並居中就好了,昨天已經做成了匯出一條條資料的excel,今天就是想在原來基礎上合併並居中再上線測試。
於是我開始了第一步,找了好多easyExcel合併單元格的文章最終確定了一個使用strategy策略用Excel.write方式去實現的,同時辨別如果是相同的就合併單元格
以下是CustomMergeStrategy的程式碼
import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import java.util.List;
public class CustomMergeStrategy implements CellWriteHandler {
@Override
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
if (isHead){
//如果是表頭不做處理
return;
}
//如果當前是第一行不做處理
if(relativeRowIndex==0){
return;
}
//獲取當前行下標,上一行下標,上一行物件,上一列物件
Sheet sheet = cell.getSheet();
int rowIndex = cell.getRowIndex();
int rowIndexPrev=rowIndex- 1;
Row row = sheet.getRow(rowIndexPrev);
Cell cellPrev = row.getCell(cell.getColumnIndex());
//得到當前單元格值和上一行單元格
// Object cellValue =cell.getCellType()== CellType.STRING?cell.getStringCellValue() : cell.getNumericCellValue();
// Object cellValuePrev =cellPrev.getCellType()==CellType.STRING?cellPrev.getStringCellValue():cellPrev.getNumericCellValue();
String cellValue =cell.getCellType()== CellType.STRING?cell.getStringCellValue() : String.valueOf(cell.getNumericCellValue());
String cellValuePrev =cellPrev.getCellType()==CellType.STRING?cellPrev.getStringCellValue():String.valueOf(cellPrev.getNumericCellValue());
System.out.println("cellValuePrev = " + cellValuePrev);
System.out.println("cellValue = " + cellValue);
//如果當前單元格和上一行單元格值相等就向下執行合併
// 這邊if不能調順序
if (StringUtils.isEmpty(cellValue) && StringUtils.isEmpty(cellValuePrev)){
return;
}
if (!cellValue.equals(cellValuePrev)) {
return;
}
//獲取已有策略
List<CellRangeAddress> mergedRegions = sheet.getMergedRegions();
boolean mergen=false;
for (int i = 0; i < mergedRegions.size(); i++) {
CellRangeAddress cellAddresses = mergedRegions.get(i);
if(cellAddresses.isInRange(rowIndexPrev,cell.getColumnIndex())){
sheet.removeMergedRegion(i);
cellAddresses.setLastRow(rowIndex);
sheet.addMergedRegion(cellAddresses);
mergen=true;
break;
}
}
if (!mergen){
CellRangeAddress cellAddresses = new CellRangeAddress(rowIndexPrev, rowIndex, cell.getColumnIndex(), cell.getColumnIndex());
sheet.addMergedRegion(cellAddresses);
}
}
}
然後被我業務程式碼呼叫實現
EasyExcel.write("/app/static/使用者統計"+dateStr+".xlsx")
.head(UserTimeLengthExcelItemDTO.class)
.registerWriteHandler(new CustomMergeStrategy())
.sheet("使用者統計").doWrite(tmpUserTimeLengthExcelItemDTOList);
ok以上程式碼就簡單實現了excel匯出和合並單元格,接著我看到
這個樣子有點醜啊,至少要居中吧,於是找了很多甚至還想多謝個策略類然後把add到registerWriteHandler這個裡面去,不過沒有成功,一直報錯,繼續想辦法不斷百度百度,都不大行,那就算了我就google看下好了哇,google出來第一個說加個註解就行了,於是我便到匯出dto上面加
@HeadStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER)
在執行下果然可以,看下效果圖,
發現還有點彆扭啊,這個字居中是居中了,但是在底部不好看啊,繼續找上下居中,查了下還是註解上加
@ContentStyle(horizontalAlignment = HorizontalAlignmentEnum.CENTER, verticalAlignment = VerticalAlignmentEnum.CENTER)//內容樣式
這樣子就可以了嘛,發現還有問題,空的和空的合併了,但是他們不是一個使用者下的,沒有截圖,於是我想應該要加個判斷如果是空的就不要合併,看了下邏輯他只是對如果是空就就合併做了判斷,我就在上頭加了個空的判斷嗎,ok這樣子一個令人滿意的報表就生成好了。
上傳到線上,然後嘗試生成excel,看到報錯
ERROR c.c.platform.core.exception.GlobalExceptionHandler - 未知異常!原因是:ExcelGenerateException: java.lang.NoClassDefFoundError: Could not initialize class sun.awt.X11FontManager
沒找到,按我在本地時候以為是資料夾不存在或者沒有許可權,我就去改儲存的資料夾路徑,前後改了3次都不成功,於是我好好看了報錯內容再去google上查下發現有人說這個是沒有找到字型的原因,好吧,沒有字型我就裝字型好吧,先apt update
aliyun真的慢等了很久終於好了,再執行命令
apt-get install -y fonts-dejavu libfreetype6-dev fontconfig
也等了好久終於好了,然後嘗試繼續報錯,我想不應該呀,我先重啟容器再說,果然我再次請求生成就可以了,好吧這樣子的話我只能把當前容器再打個映象以後就以這個新映象跑了,想起之前有個人評論說的可能是你的映象是silm的原因,我想我要不要看下Dockerfile裡用的是啥,果然看到用的FROM openjdk:17-slim
,乖乖,保底的有了,我要把這個silm刪了再試試,等了一會容器起來了,在嘗試生成下,成功!
md,折騰了靠一天,終於好了,拜拜,去搞scoop了