文章目錄
- 1 思路
- 1.1 概述
- 1.2 支持的圖表類型
- 1.3 特性
- 2 準備模板
- 3 導入依賴
- 4 圖表生成工具類 ChartWithChineseExample
- 步驟 1: 準備字體文件
- 步驟 2: 注冊字體到`FontFactory`
- 步驟 3: 設置圖表具體位置的字體
- 柱狀圖:
- 餅圖:
- 折線圖:
- 完整代碼:
- 5 業務層 OfficeServicel
- 6 通用工具類 OfficeUtils
- 7 控制層 OfficeController
- 8 導出效果
1 思路
JFreeChart :
JFreeChart是一個開源的Java圖表庫,專為JAVA平臺設計,用于生成高質量的2D圖表。
1.1 概述
- JFreeChart是一個完全使用JAVA語言編寫的圖表繪制類庫。
- 它最初由David Gilbert創建,自2001年以來一直在持續開發和更新,目前已成為Java社區中廣泛使用的圖表庫之一。
- JFreeChart是一個開源項目,遵循GNU通用公共許可證(LGPL),允許在專有應用程序中使用。
1.2 支持的圖表類型
- JFreeChart支持多種圖表類型,包括但不限于:
- 餅圖(Pie charts)
- 柱狀圖(Bar charts)
- 散點圖(Scatter plots)
- 時序圖(Time series)
- 甘特圖(Gantt charts)
- 線形圖(Line charts)
- 氣泡圖(Bubble charts)
- 熱力圖(Heatmaps)
1.3 特性
- 定制能力:提供大量的定制選項,包括顏色、字體、標簽、圖例、網格線、數據點等,以滿足各種設計需求。
- 數據源:接受各種數據結構作為輸入,如數組、列表或CategoryDataset和TimeSeriesDataset對象。
- 輸出類型:支持多種輸出類型,包括Swing組件、圖像文件(PNG、JPEG)、矢量圖形文件格式(PDF、EPS、SVG)等。
- 交互性:具有一定的交互功能,如縮放、平移等。
通過 JFreeChart 創建圖表,將圖表轉換為圖像格式(如PNG或JPEG),然后將圖像解析成InputStream 寫入到Word文檔的相應位置中。
2 準備模板
3 導入依賴
<dependency><groupId>org.jfree</groupId><artifactId>jfreechart</artifactId><version>1.5.3</version></dependency>
4 圖表生成工具類 ChartWithChineseExample
在使用org.jfree.chart
庫生成圖表時,如果遇到中文無法正常顯示的問題,通常是字體設置的問題。JFreeChart默認使用的字體可能不支持中文字符。要解決這個問題,你需要指定一個支持中文的字體。以下是解決此問題的一般步驟:
步驟 1: 準備字體文件
首先,你需要一個支持中文的TrueType字體文件(.ttf
),如宋體(SimSun.ttf
)、微軟雅黑(msyh.ttf
)等。這些字體文件通常可以在Windows系統的C:\Windows\Fonts
目錄下找到,或者你可以從互聯網上下載。
字體文件包可以從這里下載:office字體文件包
步驟 2: 注冊字體到FontFactory
在你的Java程序中,使用FontFactory.register()
方法注冊你的中文字體文件。例如,如果你有SimSun.ttf
這個字體文件,可以這樣做:
/*** 注冊中文字體*/public static void registerChineseFont() {// 注冊中文字體(這里假設已經將字體文件放置在項目的resources目錄下)InputStream fontStream = ChartWithChineseExample.class.getResourceAsStream("/font/SIMSUN.TTC"); // 路徑根據實際情況調整try {Font customFont = Font.createFont(Font.TRUETYPE_FONT, fontStream).deriveFont(Font.PLAIN, 12);GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();ge.registerFont(customFont);} catch (FontFormatException | IOException e) {e.printStackTrace();}}
步驟 3: 設置圖表具體位置的字體
柱狀圖:
// 示例字體為宋體,常規,14號Font axisLabelFont = new Font("SimSun", Font.PLAIN, 14);// X軸chart.getCategoryPlot().getDomainAxis().setLabelFont(axisLabelFont);chart.getCategoryPlot().getDomainAxis().setTickLabelFont(axisLabelFont);// Y軸chart.getCategoryPlot().getRangeAxis().setLabelFont(axisLabelFont);chart.getCategoryPlot().getRangeAxis().setTickLabelFont(axisLabelFont);
餅圖:
chart.getTitle().setFont(new Font("SimSun", Font.BOLD, 18));chart.getLegend().setItemFont(new Font("SimSun", Font.PLAIN, 12));// 獲取餅圖的plot對象,以便進行進一步定制PiePlot3D plot = (PiePlot3D) chart.getPlot();// 設置標簽字體plot.setLabelFont(new Font("SimSun", Font.PLAIN, 14));// 設置無數據信息字體(如果需要)plot.setNoDataMessageFont(new Font("SimSun", Font.PLAIN, 18));
折線圖:
// 設置字體chart.getTitle().setFont(new Font("SimSun", Font.BOLD, 18));chart.getLegend().setItemFont(new Font("SimSun", Font.PLAIN, 12));CategoryPlot plot = (CategoryPlot) chart.getPlot();plot.getDomainAxis().setLabelFont(new Font("SimSun", Font.PLAIN, 12));plot.getRangeAxis().setLabelFont(new Font("SimSun", Font.PLAIN, 12));plot.getDomainAxis().setTickLabelFont(new Font("SimSun", Font.PLAIN, 12));plot.getRangeAxis().setTickLabelFont(new Font("SimSun", Font.PLAIN, 12));
完整代碼:
package com.example.demo.uitls;import lombok.extern.slf4j.Slf4j;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.PiePlot3D;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.category.DefaultCategoryDataset;
import org.jfree.data.general.DefaultPieDataset;
import org.springframework.stereotype.Component;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;/*** ChartWithChineseExample : 圖表生成工具類** @author zyw* @create 2024-06-25 16:20*/@Slf4j
@Component
public class ChartWithChineseExample {// 柱狀圖臨時文件名public final static String BAR_CHART_FILE_NAME = "BAR_CHART.png";// 餅圖臨時文件名public final static String PIE_CHART_FILE_NAME = "PIE_CHART.png";// 折線圖臨時文件名public final static String LINE_CHART_FILE_NAME = "LINE_CHART.png";public static InputStream lineChartGeneration(String title, String x, String y, DefaultCategoryDataset dataset) {registerChineseFont();JFreeChart chart = ChartFactory.createLineChart(title, // 圖表標題x, // X軸標簽y, // Y軸標簽dataset, // 數據集PlotOrientation.VERTICAL, // 圖表方向true, // 是否顯示圖例true, // 是否生成工具提示false // 是否生成URL鏈接);chart.getTitle().setFont(new Font("SimSun", Font.BOLD, 18));chart.getLegend().setItemFont(new Font("SimSun", Font.PLAIN, 14));// 示例字體為宋體,常規,14號Font axisLabelFont = new Font("SimSun", Font.PLAIN, 14);// X軸chart.getCategoryPlot().getDomainAxis().setLabelFont(axisLabelFont);chart.getCategoryPlot().getDomainAxis().setTickLabelFont(axisLabelFont);// Y軸chart.getCategoryPlot().getRangeAxis().setLabelFont(axisLabelFont);chart.getCategoryPlot().getRangeAxis().setTickLabelFont(axisLabelFont);try {// 將圖表轉換為字節數組ByteArrayOutputStream outputStream = new ByteArrayOutputStream();BufferedImage chartImage = chart.createBufferedImage(400, 300); // 設置圖像的寬高ImageIO.write(chartImage, "png", outputStream);byte[] chartBytes = outputStream.toByteArray();// 將字節數組轉換為InputStreamInputStream inputStream = new ByteArrayInputStream(chartBytes);return inputStream;} catch (IOException e) {log.error("折線圖圖生成異常");return null;}}/*** 餅圖生成** @param title 標題* @param dataset 數據集* @return*/public static InputStream pieChartGeneration(String title, DefaultPieDataset dataset) {registerChineseFont();// 使用數據集創建餅圖JFreeChart chart = ChartFactory.createPieChart3D(title, // 圖表標題dataset, // 數據集true, // 是否顯示圖例true, // 是否生成工具提示false // 是否生成URL鏈接);chart.getTitle().setFont(new Font("SimSun", Font.BOLD, 18));chart.getLegend().setItemFont(new Font("SimSun", Font.PLAIN, 12));// 獲取餅圖的plot對象,以便進行進一步定制PiePlot3D plot = (PiePlot3D) chart.getPlot();// 設置標簽字體plot.setLabelFont(new Font("SimSun", Font.PLAIN, 14));// 設置無數據信息字體(如果需要)plot.setNoDataMessageFont(new Font("SimSun", Font.PLAIN, 18));try {// 將圖表轉換為字節數組ByteArrayOutputStream outputStream = new ByteArrayOutputStream();BufferedImage chartImage = chart.createBufferedImage(400, 300); // 設置圖像的寬高ImageIO.write(chartImage, "png", outputStream);byte[] chartBytes = outputStream.toByteArray();// 將字節數組轉換為InputStreamInputStream inputStream = new ByteArrayInputStream(chartBytes);return inputStream;} catch (IOException e) {log.error("餅圖生成異常");return null;}}/*** 注冊中文字體*/public static void registerChineseFont() {// 注冊中文字體(這里假設已經將字體文件放置在項目的resources目錄下)InputStream fontStream = ChartWithChineseExample.class.getResourceAsStream("/font/SIMSUN.TTC"); // 路徑根據實際情況調整try {Font customFont = Font.createFont(Font.TRUETYPE_FONT, fontStream).deriveFont(Font.PLAIN, 12);GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();ge.registerFont(customFont);} catch (FontFormatException | IOException e) {e.printStackTrace();}}/*** 創建柱狀圖表** @param dataset 數據集* @return*/public static InputStream createChartPanel(String title, String x, String y, DefaultCategoryDataset dataset) {registerChineseFont();// 創建圖表JFreeChart chart = ChartFactory.createBarChart(title, // 圖表標題x, // X軸標簽y, // Y軸標簽dataset,PlotOrientation.VERTICAL,true, // 是否顯示圖例true, // 是否使用工具提示false // 是否生成URL鏈接);// 設置字體chart.getTitle().setFont(new Font("SimSun", Font.BOLD, 18));chart.getLegend().setItemFont(new Font("SimSun", Font.PLAIN, 12));CategoryPlot plot = (CategoryPlot) chart.getPlot();plot.getDomainAxis().setLabelFont(new Font("SimSun", Font.PLAIN, 12));plot.getRangeAxis().setLabelFont(new Font("SimSun", Font.PLAIN, 12));plot.getDomainAxis().setTickLabelFont(new Font("SimSun", Font.PLAIN, 12));plot.getRangeAxis().setTickLabelFont(new Font("SimSun", Font.PLAIN, 12));try {// 將圖表轉換為字節數組ByteArrayOutputStream outputStream = new ByteArrayOutputStream();BufferedImage chartImage = chart.createBufferedImage(400, 300); // 設置圖像的寬高ImageIO.write(chartImage, "png", outputStream);byte[] chartBytes = outputStream.toByteArray();// 將字節數組轉換為InputStreamInputStream inputStream = new ByteArrayInputStream(chartBytes);return inputStream;} catch (IOException e) {log.error("柱狀圖生成異常");return null;}}}
5 業務層 OfficeServicel
在word中遍歷所有段落,找到需要插入圖表的段落索引。
此處省略上訴已展示代碼。
/*** OfficeServiceImpl :** @author zyw* @create 2024-06-24 15:41*/
@Service
@Slf4j
public class OfficeServiceImpl implements OfficeService {private static final String HEADER_2_1 = "營養成分攝入比例";private static final String HEADER_2_2 = "心率血氧檢查";private static final String HEADER_2_3 = "睡眠質量趨勢";@Overridepublic XWPFDocument getHealthReport(HealthReportQuery query) {try {FileInputStream fileInputStream = SpringUtils.convertInputStreamToFileInputStream(resourceLoader.getResource(PERSONAL_HEALTH_REPORT_TEMPLATE).getInputStream());XWPFDocument xwpfDocument = new XWPFDocument(fileInputStream);// 插入歷史體重int index5 = OfficeUtils.findParagraphIndexByText(xwpfDocument, HEADER_2_1);insertChartOne(xwpfDocument, index5);// 插入心率檢查int index6 = OfficeUtils.findParagraphIndexByText(xwpfDocument, HEADER_2_2);insertChartTwo(xwpfDocument, index6);// 插入睡眠質量趨勢int index7 = OfficeUtils.findParagraphIndexByText(xwpfDocument, HEADER_2_3);insertChartThree(xwpfDocument, index7);return xwpfDocument;} catch (Exception e) {log.info("獲取健康報告失敗", e);return null;}}/*** 獲取文本在文檔中的索引** @param doc 文檔* @param text 文本標識* @return*/public static int findParagraphIndexByText(XWPFDocument doc, String text) {// 獲取所有段落List<XWPFParagraph> paragraphs = doc.getParagraphs();// 查找目標段落int targetParagraphIndex = -1;for (int i = 0; i < paragraphs.size(); i++) {if (paragraphs.get(i).getText().contains(text)) {targetParagraphIndex = i;break;}}return targetParagraphIndex;}/*** 插入圖表 1** @param document* @param index* @throws Exception*/public void insertChartOne(XWPFDocument document, Integer index) throws Exception {// 填充圖表數據DefaultPieDataset<String> dataset = new DefaultPieDataset<String>();dataset.setValue("碳水化合物(30%)", 30);dataset.setValue("蛋白質(30%)", 30);dataset.setValue("脂肪(25%)", 25);dataset.setValue("纖維等營養素(15%)", 15);// 創建圖表示例InputStream chartPanel = ChartWithChineseExample.pieChartGeneration("營養成分攝入比例", dataset);// 獲取所有段落List<XWPFParagraph> paragraphs = document.getParagraphs();// 在目標段落后添加一個新的段落XWPFParagraph paragraph = document.insertNewParagraph(paragraphs.get(index + 1).getCTP().newCursor().newCursor());// 設置段落的樣式和屬性,實現換行paragraph.setWordWrap(true); // 設置自動換行// 設置段落水平居中paragraph.setAlignment(ParagraphAlignment.CENTER);// 設置段落內文字(這里是空格)垂直居中paragraph.setVerticalAlignment(TextAlignment.CENTER);// 調整行距以確保圖片上下居中,這一步可能需要根據實際情況調整XWPFRun run = paragraph.createRun();run.addPicture(chartPanel, XWPFDocument.PICTURE_TYPE_PNG, ChartWithChineseExample.BAR_CHART_FILE_NAME, Units.toEMU(400), Units.toEMU(300));}/*** 插入圖表2 心率血氧** @param document* @param index*/public void insertChartTwo(XWPFDocument document, Integer index) throws Exception {// 填充圖表數據DefaultCategoryDataset dataset = new DefaultCategoryDataset();dataset.addValue(77, "心率", "2024-06-23");dataset.addValue(85, "心率", "2024-06-24");dataset.addValue(99, "心率", "2024-06-25");dataset.addValue(92.76, "血氧飽和度", "2024-06-23");dataset.addValue(98.74, "血氧飽和度", "2024-06-24");dataset.addValue(94.2, "血氧飽和度", "2024-06-25");// 創建圖表示例InputStream chartPanel = ChartWithChineseExample.createChartPanel("心率和血氧飽和度圖表", "日期", "心率(次/分)、血氧飽和度(%)", dataset);// 獲取所有段落List<XWPFParagraph> paragraphs = document.getParagraphs();// 在目標段落后添加一個新的段落XWPFParagraph paragraph = document.insertNewParagraph(paragraphs.get(index + 1).getCTP().newCursor().newCursor());// 設置段落的樣式和屬性,實現換行paragraph.setWordWrap(true); // 設置自動換行// 設置段落水平居中paragraph.setAlignment(ParagraphAlignment.CENTER);// 設置段落內文字(這里是空格)垂直居中paragraph.setVerticalAlignment(TextAlignment.CENTER);// 調整行距以確保圖片上下居中,這一步可能需要根據實際情況調整XWPFRun run = paragraph.createRun();run.addPicture(chartPanel, XWPFDocument.PICTURE_TYPE_PNG, ChartWithChineseExample.PIE_CHART_FILE_NAME, Units.toEMU(400), Units.toEMU(300));}/*** 插入圖表3 睡眠質量趨勢** @param document* @param index* @throws Exception*/public void insertChartThree(XWPFDocument document, Integer index) throws Exception {// 填充圖表數據DefaultCategoryDataset dataset = new DefaultCategoryDataset();dataset.addValue(7.8, "起床時間", "06/18");dataset.addValue(8, "起床時間", "06/19");dataset.addValue(7.5, "起床時間", "06/20");dataset.addValue(8.3, "起床時間", "06/21");dataset.addValue(9, "起床時間", "06/22");dataset.addValue(9.5, "起床時間", "06/23");dataset.addValue(23, "睡眠時間", "06/18");dataset.addValue(24, "睡眠時間", "06/19");dataset.addValue(22.6, "睡眠時間", "06/20");dataset.addValue(23.2, "睡眠時間", "06/21");dataset.addValue(21.8, "睡眠時間", "06/22");dataset.addValue(23.7, "睡眠時間", "06/23");// 創建圖表示例InputStream chartPanel = ChartWithChineseExample.lineChartGeneration("睡眠質量趨勢", "日期", "睡眠時間", dataset);// 獲取所有段落List<XWPFParagraph> paragraphs = document.getParagraphs();// 在目標段落后添加一個新的段落XWPFParagraph paragraph = OfficeUtils.insertNewParagraph(paragraphs, document,index);// 設置段落的樣式和屬性,實現換行paragraph.setWordWrap(true); // 設置自動換行// 設置段落水平居中paragraph.setAlignment(ParagraphAlignment.CENTER);// 設置段落內文字(這里是空格)垂直居中paragraph.setVerticalAlignment(TextAlignment.CENTER);// 調整行距以確保圖片上下居中,這一步可能需要根據實際情況調整XWPFRun run = paragraph.createRun();run.addPicture(chartPanel, XWPFDocument.PICTURE_TYPE_PNG, ChartWithChineseExample.LINE_CHART_FILE_NAME, Units.toEMU(400), Units.toEMU(300));}
}
6 通用工具類 OfficeUtils
package com.example.demo.uitls;import jakarta.servlet.http.HttpServletResponse;
import org.apache.poi.xwpf.usermodel.*;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;import java.lang.reflect.Field;
import java.io.*;
import java.math.BigInteger;
import java.util.*;/*** OfficeUtils : Office工具類** @author zyw* @create 2024-06-24 16:35*/public class OfficeUtils {/*** 插入新段落** @param paragraphs 段落集合* @param document 文檔* @param index 插入位置* @return 新段落*/public static XWPFParagraph insertNewParagraph(List<XWPFParagraph> paragraphs, XWPFDocument document, Integer index) {if (paragraphs.size() == index + 1) {return document.createParagraph();} else {return document.insertNewParagraph(paragraphs.get(index + 1).getCTP().newCursor().newCursor());}}/*** 設置表格寬度去除邊框** @param table 表格* @param width 寬度值*/public static void setTableWidthToRemoveBorder(XWPFTable table, Integer width) {// 去除表格邊框CTTblPr tblPr2 = table.getCTTbl().getTblPr();CTTblBorders borders2 = tblPr2.addNewTblBorders();borders2.addNewBottom().setVal(STBorder.NONE);borders2.addNewTop().setVal(STBorder.NONE);borders2.addNewLeft().setVal(STBorder.NONE);borders2.addNewRight().setVal(STBorder.NONE);borders2.addNewInsideH().setVal(STBorder.NONE);borders2.addNewInsideV().setVal(STBorder.NONE);// 設置表格整體樣式tblPr2.addNewTblW().setW(BigInteger.valueOf(width)); // 設置表格寬度}/*** 設置表格單元格寬度及文本居中** @param cell 單元格* @param width 寬度占比*/public static void setTheLandscapeHeader(XWPFTableCell cell, double width) {setsTheCellWidth(cell, width);// 獲取單元格屬性對象CTTcPr tcPr = cell.getCTTc().isSetTcPr() ? cell.getCTTc().getTcPr() : cell.getCTTc().addNewTcPr();// 設置垂直對齊方式為居中CTVerticalJc vJc = tcPr.isSetVAlign() ? tcPr.getVAlign() : tcPr.addNewVAlign();vJc.setVal(STVerticalJc.CENTER);}/*** 設置表格單元格寬度樣式靠左** @param cell 單元格* @param width 寬度占比*/public static void setsTheCellWidthLeft(XWPFTableCell cell, double width) {// 假設A4紙寬約為210mm,1mm=360EMU,則A4寬約為7920EMUint emuFor30Percent = (int) (7920 * width);CTTblWidth ctTblWidth = cell.getCTTc().addNewTcPr().addNewTcW();// 設置寬度為2000EMU,你可以根據需要調整這個值ctTblWidth.setW(BigInteger.valueOf(emuFor30Percent));// 設置寬度類型為字符單位(也可以是其他單位,如百分比等)ctTblWidth.setType(STTblWidth.PCT);// 設置垂直居中cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);for (XWPFParagraph para : cell.getParagraphs()) {//居中para.setAlignment(ParagraphAlignment.LEFT);}}/*** 設置表格單元格寬度** @param cell 單元格* @param width 寬度占比*/public static void setsTheCellWidth(XWPFTableCell cell, double width) {// 假設A4紙寬約為210mm,1mm=360EMU,則A4寬約為7920EMUint emuFor30Percent = (int) (7920 * width);CTTblWidth ctTblWidth = cell.getCTTc().addNewTcPr().addNewTcW();// 設置寬度為2000EMU,你可以根據需要調整這個值ctTblWidth.setW(BigInteger.valueOf(emuFor30Percent));// 設置寬度類型為字符單位(也可以是其他單位,如百分比等)ctTblWidth.setType(STTblWidth.PCT);// 設置垂直居中cell.setVerticalAlignment(XWPFTableCell.XWPFVertAlign.CENTER);for (XWPFParagraph para : cell.getParagraphs()) {//居中para.setAlignment(ParagraphAlignment.CENTER);}}/*** 設置表格行的高度** @param row 行* @param heightCm 高度占比*/public static void setRowHeight(XWPFTableRow row, double heightCm) {int emuForHeight = (int) (360 * heightCm);CTTrPr trPr = row.getCtRow().addNewTrPr();CTHeight ht = trPr.addNewTrHeight();ht.setVal(BigInteger.valueOf(emuForHeight));}/*** 創建表格行** @param table 表格* @param index 行索引* @return*/public static XWPFTableRow createRow(XWPFTable table, int index) {return Objects.isNull(table.getRow(index)) ? table.createRow() : table.getRow(index);}/*** 創建單元格** @param row 行* @param index 列索引* @return*/public static XWPFTableCell createCell(XWPFTableRow row, int index) {return Objects.isNull(row.getCell(index)) ? row.createCell() : row.getCell(index);}/*** 獲取文本在文檔中的索引** @param doc 文檔* @param text 文本標識* @return*/public static int findParagraphIndexByText(XWPFDocument doc, String text) {// 獲取所有段落List<XWPFParagraph> paragraphs = doc.getParagraphs();// 查找目標段落int targetParagraphIndex = -1;for (int i = 0; i < paragraphs.size(); i++) {if (paragraphs.get(i).getText().contains(text)) {targetParagraphIndex = i;break;}}return targetParagraphIndex;}/*** 對象轉Map** @param obj 對象* @return*/public static Map<String, String> objectToMap(Object obj) {Map<String, String> map = new HashMap<>();Class<?> clazz = obj.getClass();// 獲取類中所有聲明的字段(包括私有、受保護、默認、公共)Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {field.setAccessible(true); // 設置字段可訪問(如果是私有的)try {Object value = field.get(obj);String key = "${" + field.getName() + "}"; // 構造key,以${name}形式map.put(key, String.valueOf(value));} catch (IllegalAccessException e) {e.printStackTrace();}}return map;}/*** 段落文本填充** @param document 文檔* @param insertTextMap 填充內容*/public static void paragraphTextFilling(XWPFDocument document, Map<String, String> insertTextMap) {Set<String> set = insertTextMap.keySet();Iterator<XWPFParagraph> itPara = document.getParagraphsIterator();while (itPara.hasNext()) {// 獲取文檔中當前的段落文字信息XWPFParagraph paragraph = itPara.next();List<XWPFRun> run = paragraph.getRuns();// 遍歷段落文字對象for (int i = 0; i < run.size(); i++) {// 獲取段落對象if (run.get(i) == null) { //段落為空跳過continue;}String sectionItem = null;try {// 檢查段落中是否包含文本框sectionItem = run.get(i).getText(run.get(i).getTextPosition()); //段落內容} catch (Exception e) {}if (sectionItem == null) {continue;}// 遍歷自定義表單關鍵字,替換Word文檔中的內容Iterator<String> iterator = set.iterator();while (iterator.hasNext()) {// 當前關鍵字String key = iterator.next();// 替換內容sectionItem = sectionItem.replace(key, String.valueOf(insertTextMap.get(key)));}run.get(i).setText(sectionItem, 0);}}}/*** 處理Word響應** @param downloadName 下載文件名* @param inputStream 文件輸入流* @param response 響應*/public static void processingWordResponses(String downloadName,InputStream inputStream,HttpServletResponse response) {try {// 設置響應的Content-Typeresponse.setContentType("application/octet-stream");response.setCharacterEncoding("utf-8");// 設置Content-Disposition頭部,指示瀏覽器下載文件,文件名為document.docxdownloadName = new String(downloadName.getBytes("UTF-8"), "ISO-8859-1");response.setHeader("Content-Disposition", "attachment;filename=" + downloadName + ".docx");// 獲取響應的輸出流OutputStream outputStream = response.getOutputStream();byte[] buffer = new byte[4096];int bytesRead = -1;// 將InputStream中的內容寫入到OutputStream中while ((bytesRead = inputStream.read(buffer)) != -1) {outputStream.write(buffer, 0, bytesRead);}// 關閉流inputStream.close();outputStream.close();} catch (Exception e) {}}/*** word轉InputStream** @param document* @return*/public static InputStream writeDocumentToInputStream(XWPFDocument document) {try {ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();document.write(byteArrayOutputStream);byteArrayOutputStream.close();return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());} catch (IOException e) {e.printStackTrace();return null;}}
}
7 控制層 OfficeController
package com.example.demo.controller;import com.example.demo.dto.HealthReportQuery;
import com.example.demo.service.OfficeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.enums.ParameterIn;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.bind.annotation.*;/*** OfficeController : Office辦公文件控制器** @author zyw* @create 2024-06-24 15:40*/@Tag(name = "Office辦公文件控制器")
@RestController
@RequestMapping("/office")
public class OfficeController {@Resourceprivate OfficeService officeService;@GetMapping("/getHealthReportWord")@Operation(summary = "獲取健康報告Word", description = "獲取健康報告")@Parameters({@Parameter(name = "name", description = "姓名", required = true, in = ParameterIn.QUERY),@Parameter(name = "gender", description = "性別", required = true, in = ParameterIn.QUERY),@Parameter(name = "age", description = "年齡", required = true, in = ParameterIn.QUERY)})public void getHealthReportWord(HealthReportQuery query, HttpServletResponse response) {officeService.getHealthReportWord(officeService.getHealthReport(query), query, response);}}
8 導出效果
Word效果:
PDF效果: