日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

阿里的easyexcal包实现表格动态导出

發布時間:2024/1/18 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 阿里的easyexcal包实现表格动态导出 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

阿里的easyexcal包實現表格動態導出

1.介紹
在日常開發中,我們或多或少會遇到導入excal,導出excal等業務需求,那么了解這一技能就很有必要了。
市場中針對這個,我知道的有兩個包,一個是poi(Poor Obfuscation Implementation),一個是easyexcal(阿里的);但poi存在oom內存溢出的風險,在正式環境中,導出的數據往往成千上萬條,很容易就觸發oom,所以,一般情況下公司都不建議使用poi,阿里的easyexcal包的底層實現也是使用了poi,也就是說easyexcal是對poi的進一步封裝改造,成功規避了oom。
2.場景需求
現在有一個需求:實現表頭信息動態,表數據動態,表中單元格具有各自的樣式(比如背景顏色,字體樣式,字體顏色等)。
3.設計方案思路
我這里使用easyexcal包實現該需求,首先注意表頭信息是變動的,那也就意味著沒法使用easyexcal的一系列注解(就是常規的先創建一個導出的實體類,然后再加注解),這里有個坑:我最開始的想法是,創建包含所有表頭信息的導出實體,然后通過反射的技術對具體需要展示的表頭字段進行標識,然后發現easyexcal中的注解@ExcelIgnore 忽略項是沒有value屬性的,這樣就出現了尷尬的情況:反射能給字段添加或刪除注解嗎???,我查了很多資料,最后是沒找到,應該不能實現添加或刪除,但給注解中的屬性值修改是可以做到的,但@ExcelIgnore沒有屬性供我們修改,至此,該方法行不通了;然后我看了easyexcal源碼,大致知道了導出excal的流程:先渲染表頭,再渲染數據,他們都是一個個單元格,每一個單元格有自己的行列數,一個excal表就是由一個個單元格以此拼接起來的。于是,我就使用單獨給表頭數據,表體數據,需要給單元格加樣式的行列坐標數據和樣式數據。需要注意的是表頭和表體數據格式是二維數組,我是用list中套list,外層list中的元素是一行,內部list中的元素是列。

4.具體代碼實現
我這里分別使用了3.0.5和2.2.8的easyexcal包,需要注意的是3.0版本之前和之后有較大改動,其實現的方法不一樣,注意:以下案例在springboot項目中實現
4.1easyexcal3.0.5版本實現代碼
4.1.1導核心依賴

<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.5</version></dependency>

4.1.2寫一個類繼承AbstractCellWriteHandler或者也可以實現CellWriteHandler接口

public class MyCellStyleWriteHandler extends AbstractCellWriteHandler {private List<XyInfo> xyInfo;/*給樣式模板兩個*/private CellStyle style1;private CellStyle style2;public MyCellStyleWriteHandler(){}public MyCellStyleWriteHandler(List<XyInfo> xyInfo){this.xyInfo=xyInfo;}@Overridepublic void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {// 設置行高測試}@Overridepublic void afterCellDispose(CellWriteHandlerContext context) {Cell cell = context.getCell();// 拿到poi的workbookWorkbook workbook = context.getWriteWorkbookHolder().getWorkbook();// 自定義寬度處理// 自定義樣式處理// 當前事件會在 數據設置到poi的cell里面才會回調// 判斷不是頭的情況 如果是fill 的情況 這里會==null 所以用not trueif (BooleanUtils.isNotTrue(context.getHead())) {//循環樣式信息,進行橫縱坐標的匹配,給對應的單元格樣式for (XyInfo item:xyInfo) {if (cell.getRowIndex() == item.getX() && cell.getColumnIndex() == item.getY()) {//現在已經鎖定的單元格,下面只需要給樣式//樣式: 1:紅字,紫色背景;2:黃字,紅色背景// 這里千萬記住 想辦法能復用的地方把他緩存起來 一個表格最多創建6W個樣式,我這就直接樣式模板化,避免其發生if(item.getContent()==1){cell.setCellStyle(this.getCellStyle(workbook,1));}if(item.getContent()==2){cell.setCellStyle(this.getCellStyle(workbook,2));}// 由于這里沒有指定dataformat 最后展示的數據 格式可能會不太正確// 這里要把 WriteCellData的樣式清空, 不然后面還有一個攔截器 FillStyleCellWriteHandler 默認會將 WriteCellStyle 設置到// cell里面去 會導致自己設置的不一樣(很關鍵)context.getFirstCellData().setWriteCellStyle(null);}}}}//優化代碼,給個樣式方法/*** 對于可確定的樣式,進行樣式模板化,避免每一個單元格都創建一個樣式* @param workbook 工作簿對象* @param content 具體設置樣式信息* @return*/public CellStyle getCellStyle(Workbook workbook,Integer content){//樣式: 1:紅字,紫色背景;2:黃字,紅色背景if(null==this.style1 && content==1){//避免多次創建//這里有個坑:我最初的想法是自己創建CellStyle 對象,通過new的方式,然后這個地方不認他,設置完全不起效果,所以這里只能從weekbook中拿。style1=workbook.createCellStyle();Font font=workbook.createFont();//紅字font.setColor((short)016);//藍色背景style1.setFillForegroundColor((short)030);//加載字體style1.setFont(font);// 這里需要指定 FillPatternType 為FillPatternType.SOLID_FOREGROUNDstyle1.setFillPattern(FillPatternType.SOLID_FOREGROUND);}if(null==style2 && content==2){//避免多次創建style2=workbook.createCellStyle();Font font=workbook.createFont();//白字font.setColor((short)011);//紅色背景style2.setFillForegroundColor((short)016);//加載字體style2.setFont(font);// 這里需要指定 FillPatternType 為FillPatternType.SOLID_FOREGROUNDstyle2.setFillPattern(FillPatternType.SOLID_FOREGROUND);}//返回樣式模板if(content==1){return this.style1;}if (content==2){return this.style2;}return null;} }

4.1.3導出關鍵代碼

public class Test9 {public static void main(String[] args) {//你要導出的文件存放路徑和文件名字String filePath = "D:\\ttt\\Download\\";String fileName=System.currentTimeMillis() + ".xlsx";File file = new File(filePath);if (!file.exists()){file.mkdirs();}//解析表頭容器List<List<String>>headss=new LinkedList<>();//解析數據容器List<List<String>>datas=new LinkedList<>();//解析單元格樣式容器List<XyInfo>xyInfo=new LinkedList<>();//手動添加假數據模擬真實數據(表頭信息)headss.add( new ArrayList(new LinkedList<String>(Arrays.asList("A","AA"))));headss.add( new ArrayList(new LinkedList<String>(Arrays.asList("B","AAA"))));headss.add( new ArrayList(new LinkedList<String>(Arrays.asList("C","A2","A3","A4"))));//手動添加假數據模擬真實數據(標體數據)datas.add(new LinkedList<String>(Arrays.asList("aaa","bbb","")));datas.add(new LinkedList<String>(Arrays.asList("aaa","bbb","ccc")));datas.add(new LinkedList<String>(Arrays.asList("aaa","","")));//手動添加需要設置樣式的單元格信息xyInfo.add(new XyInfo(6,0,1));xyInfo.add(new XyInfo(5,1,2));EasyExcel.write(filePath+fileName)// 這里放入動態頭.head(headss).sheet("模板(sheet名字)")//加載單元格樣式.registerWriteHandler(new MyCellStyleWriteHandler(xyInfo)).doWrite(datas);System.out.println("導出成功");} }

XyInfo實體類

@Data public class XyInfo {/*** 行*/private Integer x=0;/*** 列*/private Integer y=0;/*** 樣式: 1:紅字,紫色背景;2:黃字,紅色背景*/private Integer content;public XyInfo(Integer x,Integer y,Integer content){this.x=x;this.y=y;this.content=content;} }

4.1.4效果展示


4.2easyexcal2.2.8版本實現代碼
4.2.1導核心依賴

<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.2.8</version></dependency>

4.2.2寫一個類實現CellWriteHandler接口

public class My2 implements CellWriteHandler {private List<XyInfo> xyInfo;/*給樣式模板兩個*/private CellStyle style1;private CellStyle style2;public My2(List<XyInfo> xyInfo){this.xyInfo=xyInfo;}/*** 在創建單元格之前調用*/@Overridepublic void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer columnIndex, Integer relativeRowIndex, Boolean isHead) {}/*** 在單元格創建后調用*/@Overridepublic void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {}/*** 在單元上的所有操作完成后調用*/@Overridepublic void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {// 拿到poi的workbookWorkbook workbook = cell.getSheet().getWorkbook();// 自定義寬度處理// 自定義樣式處理// 當前事件會在 數據設置到poi的cell里面才會回調// 判斷不是頭的情況 如果是fill 的情況 這里會==null 所以用not true//循環樣式信息,進行橫縱坐標的匹配,給對應的單元格樣式for (XyInfo item:xyInfo) {if (cell.getRowIndex() == item.getX() && cell.getColumnIndex() == item.getY()) {//現在已經鎖定的單元格,下面只需要給樣式//樣式: 1:紅字,紫色背景;2:黃字,紅色背景// 這里千萬記住 想辦法能復用的地方把他緩存起來 一個表格最多創建6W個樣式,我這就直接樣式模板化,避免其發生if(item.getContent()==1){cell.setCellStyle(this.getCellStyle(workbook,1));}if(item.getContent()==2){cell.setCellStyle(this.getCellStyle(workbook,2));}}}}//優化代碼,給個樣式方法/*** 對于可確定的樣式,進行樣式模板化,避免每一個單元格都創建一個樣式* @param workbook 工作簿對象* @param content 具體設置樣式信息* @return*/public CellStyle getCellStyle(Workbook workbook,Integer content){//樣式: 1:紅字,紫色背景;2:黃字,紅色背景if(null==this.style1 && content==1){//避免多次創建style1=workbook.createCellStyle();Font font=workbook.createFont();//紅字font.setColor((short)016);//藍色背景style1.setFillForegroundColor((short)030);//加載字體style1.setFont(font);// 這里需要指定 FillPatternType 為FillPatternType.SOLID_FOREGROUNDstyle1.setFillPattern(FillPatternType.SOLID_FOREGROUND);}if(null==style2 && content==2){//避免多次創建style2=workbook.createCellStyle();Font font=workbook.createFont();//白字font.setColor((short)011);//紅色背景style2.setFillForegroundColor((short)016);//加載字體style2.setFont(font);// 這里需要指定 FillPatternType 為FillPatternType.SOLID_FOREGROUNDstyle2.setFillPattern(FillPatternType.SOLID_FOREGROUND);}//返回樣式模板if(content==1){return this.style1;}if (content==2){return this.style2;}return null;} }

4.2.3導出關鍵代碼

public class Test10 {public static void main(String[] args) {//你要導出的文件存放路徑和文件名字String filePath = "D:\\ttt\\Download\\";String fileName=System.currentTimeMillis() + ".xlsx";File file = new File(filePath);if (!file.exists()){file.mkdirs();}//解析表頭容器List<List<String>>headss=new LinkedList<>();//解析數據容器List<List<String>>datas=new LinkedList<>();//解析單元格樣式容器List<XyInfo>xyInfo=new LinkedList<>();//手動添加假數據模擬真實數據(表頭信息)headss.add( new ArrayList(new LinkedList<String>(Arrays.asList("A","AA"))));headss.add( new ArrayList(new LinkedList<String>(Arrays.asList("B","AAA"))));headss.add( new ArrayList(new LinkedList<String>(Arrays.asList("C","A2","A3","A4"))));//手動添加假數據模擬真實數據(標體數據)datas.add(new LinkedList<String>(Arrays.asList("aaa","bbb","")));datas.add(new LinkedList<String>(Arrays.asList("aaa","bbb","ccc")));datas.add(new LinkedList<String>(Arrays.asList("aaa","","")));//手動添加需要設置樣式的單元格信息xyInfo.add(new XyInfo(6,0,1));xyInfo.add(new XyInfo(5,1,2));EasyExcel.write(filePath+fileName)// 這里放入動態頭.head(headss).sheet("模板(sheet名字)")//加載單元格樣式.registerWriteHandler(new My2(xyInfo)).doWrite(datas);System.out.println("導出成功");} }

4.2.3效果展示


5.總結
到此,該需求基本實現,現在就簡單說說easyexcal包中的技術,使用了攔截器技術,aop思想,動態代理等,具體的,我后續會做整理,目前還在研究源碼,最后希望此文章能給你帶來靈感。

總結

以上是生活随笔為你收集整理的阿里的easyexcal包实现表格动态导出的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。