1、環境
JDK8
POI 5.2.3
Springboot2.7
2、DEMO
pom
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.2.3</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.11.0</version></dependency>
java
@GetMapping("/export")public ResponseEntity<byte[]> exportExcel() throws IOException {// 創建工作簿和工作表XSSFWorkbook workbook = new XSSFWorkbook();XSSFSheet sheet = workbook.createSheet("Chart");// 自定義數據源List<String> xValues = Arrays.asList("A", "B", "C", "D", "E");List<Double> line1Values = Arrays.asList(10.0, 15.0, 20.0, 25.0, 30.0);List<Double> line2Values = Arrays.asList(5.0, 7.0, 9.0, 11.0, 13.0);XSSFSheet hiddenSheet = workbook.createSheet("Hidden Data");workbook.setSheetHidden(workbook.getSheetIndex(hiddenSheet), true);Row hiddenRow = hiddenSheet.createRow(0);for (int i = 0; i < xValues.size(); i++) {Cell cell = hiddenRow.createCell(i);cell.setCellValue(xValues.get(i));}for (int i = 0; i < 10; i++) {createChart("title" + i, sheet,hiddenSheet,i,xValues,line1Values,i%2 == 0 ? line2Values: null);}// 將工作簿寫入字節數組ByteArrayOutputStream outputStream = new ByteArrayOutputStream();workbook.write(outputStream);workbook.close();// 設置響應頭HttpHeaders headersResponse = new HttpHeaders();headersResponse.setContentType(MediaType.APPLICATION_OCTET_STREAM);headersResponse.setContentDispositionFormData("attachment", "chart.xlsx");return new ResponseEntity<>(outputStream.toByteArray(), headersResponse, HttpStatus.OK);}private void createChart(String title, XSSFSheet sheet, XSSFSheet hiddenSheet, int index, List<String> xValues, List<Double> line1Values, List<Double> line2Values) {XSSFDrawing drawing = sheet.createDrawingPatriarch();XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0, 5, index * 21, 15, index * 21 + 20);XSSFChart chart = drawing.createChart(anchor);chart.setTitleText(title);chart.setTitleOverlay(false);XDDFCategoryAxis xAxis = chart.createCategoryAxis(AxisPosition.BOTTOM);xAxis.setTitle(" ");XDDFValueAxis yAxis = chart.createValueAxis(AxisPosition.LEFT);yAxis.setTitle(" ");XDDFDataSource<String> xs = XDDFDataSourcesFactory.fromStringCellRange(hiddenSheet,new CellRangeAddress(0, 0, 0, xValues.size() - 1));XDDFLineChartData lineChartData = (XDDFLineChartData) chart.createData(ChartTypes.LINE, xAxis, yAxis);XDDFNumericalDataSource<Double> line1 = XDDFDataSourcesFactory.fromArray(line1Values.toArray(new Double[0]));XDDFLineChartData.Series series1 = (XDDFLineChartData.Series) lineChartData.addSeries(xs, line1);if (CollUtil.isNotEmpty(line2Values)) {XDDFNumericalDataSource<Double> line2 = XDDFDataSourcesFactory.fromArray(line2Values.toArray(new Double[0]));XDDFLineChartData.Series series2 = (XDDFLineChartData.Series) lineChartData.addSeries(xs, line2);}chart.plot(lineChartData);}
說明,封裝了一個方法,createChart。
圖表位置
XSSFClientAnchor anchor = drawing.createAnchor(dx1, dy1, dx2, dy2, col1, row1, col2, row2);
參數說明:
-
dx1
和dy1
:- 表示圖表左上角相對于單元格左上角的偏移量(以 EMU 為單位)。
- 1 EMU = 1/360000 厘米。
- 如果你不需要精確控制偏移量,可以將這兩個值設置為?
0
。
-
dx2
和dy2
:- 表示圖表右下角相對于單元格右下角的偏移量(以 EMU 為單位)。
- 同樣,如果你不需要精確控制偏移量,可以將這兩個值設置為?
0
。
-
col1
和row1
:- 表示圖表左上角所在的單元格列號和行號(從 0 開始計數)。
- 例如,
col1 = 5
?表示圖表左上角位于第 6 列(F 列),row1 = 0
?表示圖表左上角位于第 1 行。
-
col2
和row2
:- 表示圖表右下角所在的單元格列號和行號(從 0 開始計數)。
- 例如,
col2 = 15
?表示圖表右下角位于第 16 列(P 列),row2 = 20
?表示圖表右下角位于第 21 行。
hiddenSheet存在的目的是
1.?Apache POI 的圖表數據綁定機制
Apache POI 的圖表功能是基于 Excel 的底層結構設計的。在 Excel 中,圖表的數據源通常是從工作表中的單元格范圍(CellRangeAddress
)中讀取的。也就是說,圖表的 X 軸和 Y 軸數據必須綁定到某個單元格范圍,即使這些單元格并不直接顯示在工作表中。
核心原因:
- Apache POI 的?
XDDFDataSourcesFactory.fromStringCellRange()
?方法要求傳入一個有效的單元格范圍。 - 如果沒有單元格范圍,POI 不知道如何為圖表提供數據源,因此會導致圖表無法正確顯示。
所以我采用了還是用了隱藏的方式。對于整個sheet的布局
3、效果
有個小bug
這個系列1這幾個字去不掉,圖表工具提示中的“系列名稱”(如“系列1”)是由 Excel 自動生成的。雖然 Apache POI 不支持直接修改或自定義工具提示的內容
一下方法可以一試。
// 隱藏圖例(可選)XDDFChartLegend legend = chart.getOrAddLegend();legend.setPosition(LegendPosition.TOP); // 將圖例移出可視區域
對于目前項目而言,影響不大。不改動了