poi自動生成Ecxel表格和Chart圖表
最近因為業務需求,需要做poi自動匯出Ecxel表格和Chart折線圖的功能。 所以我在網上找到了一篇關於poi生成Chart圖表的部落格,程式碼很詳細,但是缺少相關注釋說明。 想要將它改造成自己需要的樣子費了不少功夫,網上關於poi生成Chart圖比較詳細的部落格又少,所以特此分享一下。
首先需要匯入jar包,這裡提供兩種版本,4.1.1和4.1.2版本的都可以,具體如下:
匯入jar包後,先來說下我想實現的功能: 在固定的模板中(看個人公司的模板是什麼樣子的),外部只需要傳入引數,即可動態生成Excel表格和相關的Chart折線圖。 下面為我想要做出的效果圖:<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>4.1.1</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml</artifactId> <version>4.1.1</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-scratchpad</artifactId> <version>4.1.1</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-examples</artifactId> <version>4.1.1</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-excelant</artifactId> <version>4.1.1</version> </dependency> <dependency> <groupId>org.apache.poi</groupId> <artifactId>ooxml-schemas</artifactId> <version>1.4</version> </dependency> <!-- https://mvnrepository.com/artifact/fr.opensagres.xdocreport/xdocreport --> <dependency> <groupId>fr.opensagres.xdocreport</groupId> <artifactId>xdocreport</artifactId> <version>1.0.6</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.xmlbeans/xmlbeans --> <dependency> <groupId>org.apache.xmlbeans</groupId> <artifactId>xmlbeans</artifactId> <version>3.1.0</version> </dependency> <!-- https://mvnrepository.com/artifact/commons-codec/commons-codec --> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.13</version> </dependency> <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-compress --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-compress</artifactId> <version>1.19</version> </dependency> <!-- https://mvnrepository.com/artifact/dom4j/dom4j --> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency>
由上圖可以看出來,我們只需要傳入相關的引數,即可自動生成對應數量的Sheet頁,並在Sheet頁中自動生成Excel表和相關的折線圖。
好了,下面直接上程式碼:
首先根據模板初始化資料,同時這也是我的測試類:
public static void main(String[] args) { String filePath = "E:\\programming\\備份資料\\NewChart.xlsx"; //檔案路徑 List<String> sheetNameArr = new ArrayList<>(Arrays.asList("總體分析","承保")); //Sheet頁名稱 ArrayList<String> titleArr = new ArrayList<>(Arrays.asList("月份","1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"));//表頭欄位 NewTestCharUtil.setSheetNameArr(sheetNameArr); NewTestCharUtil.setTitleArr(titleArr); Map<String,List> dataMap = new HashMap<>();//資料集合 List<BaseFormMap> dataList = new ArrayList<>(); //資料集合1 BaseFormMap baseFormMap1 = new BaseFormMap(); baseFormMap1.put("value1","2019年運維"); baseFormMap1.put("value2",1245); baseFormMap1.put("value3",1908); baseFormMap1.put("value4",345); baseFormMap1.put("value5",3456); baseFormMap1.put("value6",167); baseFormMap1.put("value7",856); baseFormMap1.put("value8",2345); baseFormMap1.put("value9",453); baseFormMap1.put("value10",2343); baseFormMap1.put("value11",785); baseFormMap1.put("value12",723); baseFormMap1.put("value13",567); BaseFormMap baseFormMap2 = new BaseFormMap(); baseFormMap2.put("value1","2020年運維"); baseFormMap2.put("value2",4562); baseFormMap2.put("value3",1234); baseFormMap2.put("value4",543); baseFormMap2.put("value5",237); baseFormMap2.put("value6",628); baseFormMap2.put("value7",231); baseFormMap2.put("value8",894); baseFormMap2.put("value9",786); baseFormMap2.put("value10",0); baseFormMap2.put("value11",0); baseFormMap2.put("value12",0); baseFormMap2.put("value13",0); BaseFormMap baseFormMap3 = new BaseFormMap(); baseFormMap3.put("value1","2020年開發"); baseFormMap3.put("value2",234); baseFormMap3.put("value3",243); baseFormMap3.put("value4",562); baseFormMap3.put("value5",1278); baseFormMap3.put("value6",512); baseFormMap3.put("value7",675); baseFormMap3.put("value8",444); baseFormMap3.put("value9",121); baseFormMap3.put("value10",0); baseFormMap3.put("value11",0); baseFormMap3.put("value12",0); baseFormMap3.put("value13",0); dataList.add(baseFormMap1); dataList.add(baseFormMap2); dataList.add(baseFormMap3); List<BaseFormMap> dataList2 = new ArrayList<>(); //資料集合2 BaseFormMap baseFormMap4 = new BaseFormMap(); baseFormMap4.put("value1","2019年運維"); baseFormMap4.put("value2",123); baseFormMap4.put("value3",2378); baseFormMap4.put("value4",5474); baseFormMap4.put("value5",6734); baseFormMap4.put("value6",223); baseFormMap4.put("value7",546); baseFormMap4.put("value8",123); baseFormMap4.put("value9",999); baseFormMap4.put("value10",234); baseFormMap4.put("value11",456); baseFormMap4.put("value12",234); baseFormMap4.put("value13",678); BaseFormMap baseFormMap5 = new BaseFormMap(); baseFormMap5.put("value1","2020年運維"); baseFormMap5.put("value2",123); baseFormMap5.put("value3",3453); baseFormMap5.put("value4",567); baseFormMap5.put("value5",345); baseFormMap5.put("value6",5478); baseFormMap5.put("value7",567); baseFormMap5.put("value8",343); baseFormMap5.put("value9",899); baseFormMap5.put("value10",1233); baseFormMap5.put("value11",0); baseFormMap5.put("value12",0); baseFormMap5.put("value13",2342); BaseFormMap baseFormMap6 = new BaseFormMap(); baseFormMap6.put("value1","2020年開發"); baseFormMap6.put("value2",678); baseFormMap6.put("value3",454); baseFormMap6.put("value4",123); baseFormMap6.put("value5",1278); baseFormMap6.put("value6",456); baseFormMap6.put("value7",893); baseFormMap6.put("value8",123); baseFormMap6.put("value9",6734); baseFormMap6.put("value10",0); baseFormMap6.put("value11",234); baseFormMap6.put("value12",0); baseFormMap6.put("value13",0); dataList2.add(baseFormMap4); dataList2.add(baseFormMap5); dataList2.add(baseFormMap6); dataMap.put("dataList",dataList); dataMap.put("dataList2",dataList2); NewTestCharUtil.setDataMap(dataMap); boolean result = NewTestCharUtil.createChart(filePath,true,"line",dataMap); System.out.println(result); }
NewTestCharUtil是我建立的poi工具類,其中定義了兩個主要的方法,分別是建立Excel和建立Chart圖的方法,如下:
public class NewTestCharUtil { private static Map<String,List> dataMap; //用於存放資料集 private static List<String> sheetNameArr; //用於存放Sheet頁名字 private static List<String> titleArr; //用於存放Excel表頭欄位 /** * 建立Excel資料表 * @param workbook 工作簿物件 * @param sheet Sheet頁物件 * @param sheetName Sheet頁名稱 * @param fldNameArr 欄位名 * @param dataList 資料集合 * @param titleArr 標題集合 * @return */ public static boolean createExcel(XSSFWorkbook workbook, XSSFSheet sheet, String sheetName, List<String> fldNameArr, List<BaseFormMap> dataList, List<String> titleArr){ //第一行標題樣式 XSSFCellStyle title1Style = workbook.createCellStyle(); title1Style.setFillForegroundColor(IndexedColors.ROYAL_BLUE.getIndex()); //單元格填充色(藍色) title1Style.setFillPattern(FillPatternType.SOLID_FOREGROUND); //填充圖案 title1Style.setAlignment(HorizontalAlignment.CENTER); //水平對齊方式(居中) //第二行標題樣式 XSSFCellStyle title2Style = workbook.createCellStyle(); title2Style.setFillForegroundColor(IndexedColors.ROYAL_BLUE.getIndex()); //單元格填充色(藍色) title2Style.setFillPattern(FillPatternType.SOLID_FOREGROUND); //填充圖案 title2Style.setBorderBottom(BorderStyle.THIN); //下邊框 title2Style.setBorderTop(BorderStyle.THIN); //上邊框 title2Style.setBorderLeft(BorderStyle.THIN); //左邊框 title2Style.setBorderRight(BorderStyle.THIN); //右邊框 title2Style.setAlignment(HorizontalAlignment.CENTER); //水平對齊方式(居中) //資料行單元格樣式 XSSFCellStyle cellStyle = workbook.createCellStyle(); cellStyle.setBorderBottom(BorderStyle.THIN); //下邊框 cellStyle.setBorderTop(BorderStyle.THIN); //上邊框 cellStyle.setBorderLeft(BorderStyle.THIN); //左邊框 cellStyle.setBorderRight(BorderStyle.THIN); //右邊框 cellStyle.setAlignment(HorizontalAlignment.CENTER); //水平對齊方式(居中) //資料行第一個單元格樣式 XSSFCellStyle cell1Style = workbook.createCellStyle(); cell1Style.setBorderBottom(BorderStyle.THIN); //下邊框 cell1Style.setBorderTop(BorderStyle.THIN); //上邊框 cell1Style.setBorderLeft(BorderStyle.THIN); //左邊框 cell1Style.setBorderRight(BorderStyle.THIN); //右邊框 cell1Style.setAlignment(HorizontalAlignment.CENTER); //水平對齊方式(居中) //列寬自適應 for (int i=1; i<fldNameArr.size(); i++){ sheet.autoSizeColumn(i,true); } //設定第一列單元格寬度 sheet.setColumnWidth(0,sheet.getColumnWidth(0)*25/10); //建立第一行 XSSFRow tableTitleNameRow = sheet.createRow(0); XSSFCell tableTitleName = tableTitleNameRow.createCell(0); tableTitleName.setCellValue(sheetName); //第一行標題單元格賦值 //合併第一行單元格 CellRangeAddress cra = new CellRangeAddress(0,0,0,fldNameArr.size()-1); sheet.addMergedRegion(cra); //對合並後的單元格進行樣式設定 RegionUtil.setBorderBottom(BorderStyle.THIN,cra,sheet); RegionUtil.setBorderTop(BorderStyle.THIN,cra,sheet); RegionUtil.setBorderLeft(BorderStyle.THIN,cra,sheet); RegionUtil.setBorderRight(BorderStyle.THIN,cra,sheet); //對合並後的單元格進行居中 sheet.getRow(0).getCell(0).setCellStyle(title1Style); XSSFRow tableTitleRow = sheet.createRow(1); //建立第二行 //遍歷標題集合 for(int i=0; i<titleArr.size(); i++){ tableTitleRow.createCell(i).setCellValue((String) titleArr.get(i)); //第二行標題賦值 tableTitleRow.getCell(i).setCellStyle(title2Style); //第二行標題設定樣式 } int rowIndex = 2; //資料行計數器(從第三行開始建立) //遍歷建立資料行 for(BaseFormMap dataMap:dataList){ XSSFRow row = sheet.createRow(rowIndex); int cellIndex = 0; //單元格計數器 //遍歷欄位名,將值從Map中取出 for(String valueName:fldNameArr){ if(cellIndex==0){ row.createCell(cellIndex).setCellValue((String)dataMap.get(valueName)); //對資料行單元格進行賦值 }else{ row.createCell(cellIndex).setCellValue((int)dataMap.get(valueName)); //對資料行單元格進行賦值 } row.getCell(cellIndex).setCellStyle(cellStyle); //資料單元格設定樣式 cellIndex++; //單元格計數器 } rowIndex++; //行計數器自增 } return true; } /** * 建立Chart圖表 * @param fullPath 檔案儲存路徑 * @param isCreated 是否建立Chart * @param type Chart圖型別 * @param dataMap 資料集合 * @return */ public static boolean createChart(String fullPath, boolean isCreated, String type, Map<String,List> dataMap){ //定義工作簿物件 XSSFWorkbook workbook = new XSSFWorkbook(); //定義返回值 boolean result = false; //定義流物件 FileOutputStream fos = null; try { //判斷資料量和sheet量是否相等 if(dataMap.keySet().size()!=sheetNameArr.size()){ System.out.println("dataMap資料量和sheet量不相等,無法建立!"); return result; } //判斷全路徑是否為空 if(fullPath!=null || !"".equals(fullPath)){ //獲取需要建立的Excel型別 String excelType = fullPath.substring(fullPath.lastIndexOf(".")); if(!".xlsx".equals(excelType)){ System.out.println("需建立的檔案型別不是xlsx,建立失敗!"); } }else { System.out.println("全路徑不能為空!"); return false; } //用於接收dataMap集合的鍵集 List<String> dataMapKeyArr = new ArrayList<>(); //迴圈遍歷dataMap的鍵集 for(String key:dataMap.keySet()){ dataMapKeyArr.add(key); } //dataMap的鍵集的計數器 int mapKeyIndex = 0; //判斷是否建立Chart if(isCreated){ //判斷Chart圖型別是否正確 if("".equals(type) || null==type || !"line".equals(type)){ System.out.println("Chart圖型別不正確,無法建立!"); return false; } //定義畫布物件 XSSFDrawing drawing = null; //遍歷SheetNameArr,用於多次建立Sheet物件 for (String sheetName:sheetNameArr){ //判斷Sheet頁名字是否為空 if(sheetName==null || "".equals(sheetName)){ System.out.println("Sheet頁名字不能為空!"); return false; } //建立資料集合 List<BaseFormMap> dataList = new ArrayList<>(); dataList = dataMap.get(dataMapKeyArr.get(mapKeyIndex)); mapKeyIndex++; //鍵集計數器自增 //獲取BaseFormMap的鍵集 List<String> fldNameArr = new ArrayList<>(); if(fldNameArr.size()==0){ BaseFormMap baseFormMap = dataList.get(0); for (Object key: baseFormMap.keySet()){ fldNameArr.add((String) key); } } //建立Sheet物件 XSSFSheet sheet = workbook.createSheet(sheetName); //建立Excel表格 result = createExcel(workbook, sheet, sheetName, fldNameArr, dataList, titleArr); if(!result){ System.out.println("Excel建立失敗!"); return result; } //建立一個畫布 drawing = sheet.createDrawingPatriarch(); //前四個引數是畫布位置偏移量引數,後四個引數是畫布大小引數(col1:畫布左上角位置;row1:從哪行開始建立畫布;col2:畫布寬度;row2:畫布高度) XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 0, dataMap.size()+4, (dataList.size()-1)*6, 27); //建立一個Chart物件 XSSFChart chart = drawing.createChart(anchor); //建立一個CTChar物件 CTChart ctChart = chart.getCTChart(); //建立繪圖區 CTPlotArea ctPlotArea = ctChart.getPlotArea(); //判斷需要建立的Chart圖型別 if("line".equals(type)){ //建立折線圖 CTLineChart ctLineChart = ctPlotArea.addNewLineChart(); CTBoolean ctBoolean = ctLineChart.addNewVaryColors(); ctLineChart.addNewGrouping().setVal(STGrouping.STANDARD); //STANDARD標準模式/STACKED堆疊模式(不要使用STACKED堆疊模式) //建立序列,並且設定選中區域 for (int i = 0; i < dataList.size(); i++) { CTLineSer ctLineSer = ctLineChart.addNewSer(); CTSerTx ctSerTx = ctLineSer.addNewTx(); //圖例區 CTStrRef ctStrRef = ctSerTx.addNewStrRef(); //引數1:從哪行開始獲取資料 引數2:獲取到哪行結束 引數3:從哪個單元格開始獲取資料 引數4:獲取到哪個單元格結束 String legendDataRange = new CellRangeAddress(i+2,i+2, 0, 0).formatAsString(sheetName, true); ctStrRef.setF(legendDataRange); ctStrRef.setF(legendDataRange); ctLineSer.addNewIdx().setVal(i); //橫座標區 CTAxDataSource cttAxDataSource = ctLineSer.addNewCat(); ctStrRef = cttAxDataSource.addNewStrRef(); //引數1:從哪行開始獲取資料 引數2:獲取到哪行結束 引數3:從哪個單元格開始獲取資料 引數4:獲取到哪個單元格結束 String axisDataRange = new CellRangeAddress(1, 1, 1, dataList.get(i).values().size()).formatAsString(sheetName, true); ctStrRef.setF(axisDataRange); //資料區域 CTNumDataSource ctNumDataSource = ctLineSer.addNewVal(); CTNumRef ctNumRef = ctNumDataSource.addNewNumRef(); //引數1:從哪行開始獲取資料 引數2:獲取到哪行結束 引數3:從哪個單元格開始獲取資料 引數4:獲取到哪個單元格結束 String numDataRange = new CellRangeAddress(i+2, i+2, 1, dataList.get(i).values().size()).formatAsString(sheetName, true); ctNumRef.setF(numDataRange); //設定標籤格式 ctBoolean.setVal(false); CTDLbls newDLbls = ctLineSer.addNewDLbls(); newDLbls.setShowLegendKey(ctBoolean);//折線節點上是否顯示圖例 ctBoolean.setVal(true); newDLbls.setShowVal(ctBoolean);//折線節點上是否顯示值 ctBoolean.setVal(false); newDLbls.setShowCatName(ctBoolean); newDLbls.setShowSerName(ctBoolean); //newDLbls.setShowPercent(ctBoolean); //newDLbls.setShowBubbleSize(ctBoolean); //newDLbls.setShowLeaderLines(ctBoolean); //是否開啟平滑曲線 CTBoolean addNewSmooth = ctLineSer.addNewSmooth(); addNewSmooth.setVal(false); //是否是堆積曲線 CTMarker addNewMarker = ctLineSer.addNewMarker(); CTMarkerStyle addNewSymbol = addNewMarker.addNewSymbol(); //設定節點形狀:CIRCLE:圓形節點 DASH:破折號節點(短橫線)DIAMOND:菱形節點 DOT:短橫線節點(很短)PICTURE:圖表為圖片 PLUS:+節點 SQUARE:方塊節點 STAR:*形節點 TRIANGLE:三角形節點 X:X形節點 addNewSymbol.setVal(STMarkerStyle.NONE); } //telling the BarChart that it has axes and giving them Ids ctLineChart.addNewAxId().setVal(123456); ctLineChart.addNewAxId().setVal(123458); //設定X軸 CTCatAx ctCatAx = ctPlotArea.addNewCatAx(); ctCatAx.addNewAxId().setVal(123456); //id of the cat axis CTScaling ctScaling = ctCatAx.addNewScaling(); ctScaling.addNewOrientation().setVal(STOrientation.MIN_MAX);//MAX_MIN:X軸從左到右由大到小(會導致Y軸顯示在右側,影象左右反轉) MIN_MAX:正常顯示 ctCatAx.addNewAxPos().setVal(STAxPos.L); ctCatAx.addNewCrossAx().setVal(123458); //id of the val axis ctCatAx.addNewTickLblPos().setVal(STTickLblPos.NEXT_TO); //HIGH:X軸座標顯示在頂部 LOW:X軸座標正常顯示 NEXT_TO:X軸座標正常顯示 //設定Y軸 CTValAx ctValAx = ctPlotArea.addNewValAx(); ctValAx.addNewAxId().setVal(123458); //id of the val axis ctScaling = ctValAx.addNewScaling(); ctScaling.addNewOrientation().setVal(STOrientation.MIN_MAX); //MAX_MIN:Y軸座標從上到下由小到大(會導致X軸顯示在頂部,影象上下反轉) MIN_MAX:正常顯示 ctValAx.addNewAxPos().setVal(STAxPos.B); ctValAx.addNewCrossAx().setVal(123456); //id of the cat axis ctValAx.addNewTickLblPos().setVal(STTickLblPos.NEXT_TO); //HIGH:Y軸刻度左側顯示,座標值右側顯示 LOW:Y軸座標正常顯示 NEXT_TO:Y軸座標正常顯示 ctValAx.addNewDelete().setVal(false);//是否隱藏Y軸 ctCatAx.addNewDelete().setVal(false);//是否隱藏X軸 //legend圖注 CTLegend ctLegend = ctChart.addNewLegend(); ctLegend.addNewLegendPos().setVal(STLegendPos.B); ctLegend.addNewOverlay().setVal(false); } } }else{ //遍歷SheetNameArr,用於多次建立Sheet物件 for (String sheetName:sheetNameArr){ //建立Sheet物件 XSSFSheet sheet = workbook.createSheet(sheetName); List<BaseFormMap> dataList = new ArrayList<>(); dataList = dataMap.get(dataMapKeyArr.get(mapKeyIndex)); //獲取BaseFormMap的鍵集 List<String> fldNameArr = new ArrayList<>(); if(fldNameArr.size()==0){ BaseFormMap baseFormMap = dataList.get(0); for (Object key: baseFormMap.keySet()){ fldNameArr.add((String) key); } } //建立Excel表格 result = createExcel(workbook, sheet, sheetName, fldNameArr, dataList, titleArr); if(!result){ System.out.println("Excel建立失敗!"); return result; } mapKeyIndex++; //鍵集計數器自增 } } fos = new FileOutputStream(fullPath); workbook.write(fos); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } finally { if(null!=fos){ try { fos.close(); } catch (IOException e) { e.printStackTrace(); } } } return result; } public static Map<String, List> getDataMap() {return dataMap;} public static void setDataMap(Map<String, List> dataMap) {NewTestCharUtil.dataMap = dataMap;} public static List<String> getSheetNameArr() {return sheetNameArr;} public static void setSheetNameArr(List<String> sheetNameArr) {NewTestCharUtil.sheetNameArr = sheetNameArr;} public static List<String> getTitleArr() {return titleArr;} public static void setTitleArr(List<String> titleArr) {NewTestCharUtil.titleArr = titleArr;} }
以上程式碼執行之後,就能創建出本部落格一開始想要實現的效果。 因為我暫時只實現了一個折線圖,所以目前只能圖表型別只能傳"line",後期等我實現了其他形式的chart圖,我再回來補充。 在使用的時候,有幾個需要注意的點: 1.`STGrouping.STANDAR`用於設定Chart圖的模式,分為標準模式和堆疊模式,折線圖建立的時必須用標準模式,使用堆疊模式會使折線出現偏差; 2.`addNewSmooth.setVal(false);`設定為ture可以讓原本有稜有角的折線圖,變得如牛奶般絲滑.... 3.`STMarkerStyle.NONE`是折線圖中折點的形狀,具體的可以參照上面註釋中的值設定(誰讓我閒得慌把所有折點形狀都試了一遍呢~) 4.`createChart()`方法中傳入的isCreated,是建立chart圖的開關,傳入false可以只建立Excel,不建立chart,傳入ture可以同時建立; 5.最重要的一點:chart圖建立時,是根據我們提供的選值範圍座標,從之前建立的Excel圖選定需要遍歷的資料範圍,然後自動建圖,建圖正確與否、數值獲取正確與否的關鍵,在於我們提供的選值範圍座標!
有興趣的還可以再加上一段根據路徑中檔案字尾,自動建立對應型別Excel檔案的程式碼,這個比較簡單,我就不贅述了。