在企业开发中,经常碰到需要生成报告的需求。

添加依赖

1
2
3
4
5
6
7
8
9
<dependency>  
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
</dependency>

<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>ooxml-schemas</artifactId>
</dependency>

编写操作工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
@Slf4j  
public class WordUtil {

// localhost:8081/word/export
public static final int widthTable = 9072;

/**
* 创建一个Word文档实体
*/
public static XWPFDocument createDocument() {
XWPFDocument document = new XWPFDocument();
return document;
}

/**
* 处理Word文档纸张大小(默认A4纸)
*/ public static void handlePaperSize(XWPFDocument document) {
CTSectPr sectPr = document.getDocument().getBody().addNewSectPr();
CTPageSz pgsz = sectPr.isSetPgSz() ? sectPr.getPgSz() : sectPr.addNewPgSz();
pgsz.setW(BigInteger.valueOf(11906));
pgsz.setH(BigInteger.valueOf(16838));
}

/**
* 处理页面边距
*/
public static void handlePagerMargin(XWPFDocument document) {
CTSectPr sectPr = document.getDocument().getBody().addNewSectPr();
CTPageMar ctPageMar = sectPr.addNewPgMar();
// 上边距
ctPageMar.setTop(BigInteger.valueOf(1440));
// 下边距
ctPageMar.setBottom(BigInteger.valueOf(1440));
// 左边距
ctPageMar.setLeft(BigInteger.valueOf(1440));
// 右边距
ctPageMar.setRight(BigInteger.valueOf(1440));
}

/**
* 添加空行
*/
public static void handleBlankRow(XWPFDocument document) {
XWPFParagraph emptyParagraph5 = document.createParagraph();
emptyParagraph5.setSpacingAfter(200);
}

/**
* 添加段落
*/
public static XWPFParagraph handleAddParagraph(XWPFDocument document, String content, String fontFamily, int fontSize, ParagraphAlignment alignment, boolean bold) {
XWPFParagraph paragraph = document.createParagraph();
paragraph.setAlignment(alignment);
XWPFRun topical = paragraph.createRun();
topical.setText(content);
topical.setFontFamily(fontFamily);
topical.setFontSize(fontSize);
topical.setBold(bold);
return paragraph;
}

/**
* 设置段落外边距
*/
public static void handleMargins(XWPFParagraph paragraph, int before, int after) {
CTP ctp = paragraph.getCTP();
CTPPr ctpPr = ctp.isSetPPr() ? ctp.getPPr() : ctp.addNewPPr();
CTSpacing ctSpacing = ctpPr.isSetSpacing() ? ctpPr.getSpacing() : ctpPr.addNewSpacing();
ctSpacing.setBefore(BigInteger.valueOf(before));
ctSpacing.setAfter(BigInteger.valueOf(after));
}

/**
* 设置段落的首行缩进两个字符
*/
public static void handleFirstLineIndentation(XWPFParagraph paragraph, int fontSize) {
paragraph.setAlignment(ParagraphAlignment.LEFT);
// 动态计算缩进值(每一点20字宽 twip,一个中文字符宽度为 字号*20 twipx)
int firstLineIndent = fontSize * 20 * 2;
paragraph.setIndentationFirstLine(firstLineIndent);
}

/**
* 创建表格
*/
public static XWPFTable createTable(XWPFDocument document, int row, int column) {
XWPFTable table = document.createTable(row, column);
CTTbl tbl = table.getCTTbl();
tbl.getTblPr().addNewTblW().setW(BigInteger.valueOf(widthTable));
return table;
}

/**
* 设置表格行间距
*/
public static void handleRowSpacing(XWPFTable table, int height) {
List<XWPFTableRow> rows = table.getRows();
for (XWPFTableRow row : rows) {
row.setHeight(height);
}
}
/**
* 设置单元格内容
*/
public static void handleCellContent(XWPFTableCell keyCell, String content, String fontFamily, int fontSize) {
CTTc ctTc1 = keyCell.getCTTc();
CTTcPr ctTcPr1 = ctTc1.addNewTcPr();
// 设置单元格垂直居中
CTVerticalJc ctVerticalJc1 = ctTcPr1.addNewVAlign();
ctVerticalJc1.setVal(STVerticalJc.CENTER);
List<XWPFParagraph> paragraphs = keyCell.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
paragraph.setAlignment(ParagraphAlignment.CENTER);
XWPFRun keyRun = paragraph.createRun();
keyRun.setText(content);
keyRun.setFontFamily(fontFamily);
keyRun.setFontSize(fontSize);
}
}
/**
* 处理表格单行单元格宽度
*/
public static void handleCellWidth(XWPFTableRow row, HashMap<Integer, Integer> map) {
if (CollectionUtil.isEmpty(map)) {
return;
}
List<XWPFTableCell> tableCells = row.getTableCells();
int cellSum = map.size();
int size = tableCells.size();
int width = widthTable / (size - cellSum);
int remainder = widthTable % (size - cellSum);
for (int i = 0; i < tableCells.size(); i++) {
Integer cellWidth = map.get(i);
if (cellWidth == null) {
cellWidth = width;
}
cellWidth = i == 0 ? cellWidth + remainder : cellWidth;
XWPFTableCell cell = tableCells.get(i);
CTTc ctTc = cell.getCTTc();
CTTcPr ctTcPr = ctTc.addNewTcPr();
ctTcPr.addNewTcW().setW(BigInteger.valueOf(cellWidth));
}
}
/**
* 设置行高
*/
public static void handleCellHeight(XWPFTableRow row, int height) {
row.setHeight(height);
}

/**
* 合并单元格内容
*/
public static void handleMergeCell(XWPFTableRow row, int start, int end) {
XWPFTableCell cell = row.getTableCells().get(start);
CTTc ctTc = cell.getCTTc();
CTTcPr ctTcPr = ctTc.addNewTcPr();
ctTcPr.addNewHMerge().setVal(STMerge.RESTART);
int space = end - start;
while (space > 0) {
XWPFTableCell cell1 = row.getTableCells().get(++start);
CTTc ctTc1 = cell1.getCTTc();
CTTcPr ctTcPr1 = ctTc1.addNewTcPr();
ctTcPr1.addNewHMerge().setVal(STMerge.CONTINUE);
space--;
}
}
/**
* 设置单元格文字位置
*/
public static void handleCellStyle(XWPFTableCell keyCell, STVerticalJc.Enum vertical, ParagraphAlignment alignment) {
CTTc ctTc1 = keyCell.getCTTc();
CTTcPr ctTcPr1 = ctTc1.addNewTcPr();
CTVerticalJc ctVerticalJc1 = ctTcPr1.addNewVAlign();
ctVerticalJc1.setVal(STVerticalJc.CENTER);
List<XWPFParagraph> paragraphs = keyCell.getParagraphs();
for (XWPFParagraph paragraph : paragraphs) {
paragraph.setAlignment(alignment);
}
}
/**
* 段落内容图片插入
*/
@SneakyThrows
public static void handleCellImage(XWPFParagraph paragraph, MultipartFile multipartFile) {
if (multipartFile == null) {
return;
}
@Cleanup InputStream inputStream = multipartFile.getInputStream();
@Cleanup ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
//压缩图片 减少导出的图片大小
Thumbnails.of(inputStream).imageType(BufferedImage.TYPE_INT_RGB).scale(0.5).outputQuality(0.8).toOutputStream(byteArrayOutputStream);
@Cleanup ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
double desiredWidthCm = 15.56; // 目标宽度(厘米)
double desiredHeightCm = 11.12; // 目标高度(厘米)
// 将厘米转换为 EMU int widthEMU = (int) (desiredWidthCm * 360000);
int heightEMU = (int) desiredHeightCm * 360000;
XWPFRun coverRun = paragraph.createRun();
coverRun.addPicture(byteArrayInputStream, XWPFDocument.PICTURE_TYPE_JPEG,
new File(Objects.requireNonNull(multipartFile.getOriginalFilename())).getName(),
widthEMU, heightEMU);
}

@SneakyThrows
public static void handleCellImage(XWPFRun run, InputStream inputStream, String filename, int widthEMU, int heightEMU) {
@Cleanup ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
//压缩图片 减少导出的图片大小
Thumbnails.of(inputStream).imageType(BufferedImage.TYPE_INT_RGB).scale(0.5).outputQuality(0.8).toOutputStream(byteArrayOutputStream);
@Cleanup ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
run.addPicture(byteArrayInputStream, XWPFDocument.PICTURE_TYPE_JPEG,
filename,
widthEMU, heightEMU);
}

/**
* 文件导出到输入流
*/
@SneakyThrows
public static void exportWord(XWPFDocument document, ByteArrayOutputStream outputStream) {
document.write(outputStream);
}

生成文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
@SneakyThrows(value = Exception.class)  
private void testWord(HttpServletResponse response) {
String fontFamily = "宋体";
XWPFDocument document = WordUtil.createDocument();
WordUtil.handlePaperSize(document);
WordUtil.handlePagerMargin(document);
XWPFParagraph title = WordUtil.handleAddParagraph(document, "Word功能测试报告", fontFamily, 22, ParagraphAlignment.CENTER, true);
WordUtil.handleMargins(title, 120, 120);
XWPFParagraph title1 = WordUtil.handleAddParagraph(document, "1.段落摘录", fontFamily, 18, ParagraphAlignment.LEFT, true);
WordUtil.handleMargins(title1, 120, 120);
String content = "阳春三月,风和日暖;信步城外,看阡陌之上杨柳依依,野花绚烂,身心不由得轻爽而浪漫。漫步陌上,只因陌上花开;花是自然的那种,朴素而恬淡,不落尘俗。“三月风情陌上花”,是花在其中生命得以璀璨,人在其心心情得以畅然的一种意境。这意境,枝繁叶茂,从古代长到现代,不枯不衰;又如水,岁岁年年,流淌在阡陌之上,不知迷醉过古今几个王公贵族、粉黛佳丽、骚人墨客、凡男俗女。三月陌上花,让人爱让人痴,恍惚人的骨子里都沉淀了花的影子,花的风韵。";
XWPFParagraph paragraph = WordUtil.handleAddParagraph(document, content, fontFamily, 14, ParagraphAlignment.LEFT, false);
WordUtil.handleMargins(paragraph, 120, 120);
WordUtil.handleFirstLineIndentation(paragraph, 16);
WordUtil.handleBlankRow(document);
XWPFParagraph title2 = WordUtil.handleAddParagraph(document, "2.表格处理", fontFamily, 18, ParagraphAlignment.LEFT, true);
WordUtil.handleMargins(title2, 120, 120);
XWPFTable table = WordUtil.createTable(document, 4, 5);
WordUtil.handleRowSpacing(table, 454);
List<XWPFTableRow> rows = table.getRows();
for (int i = 0; i < rows.size(); i++) {
XWPFTableRow row = rows.get(i);
List<XWPFTableCell> tableCells = row.getTableCells();
if (i == 0) {
String[] headers = {"序号", "姓名", "年龄", "爱好", "职业"};
for (int j = 0; j < tableCells.size(); j++) {
XWPFTableCell cell = tableCells.get(j);
WordUtil.handleCellContent(cell, headers[j], fontFamily, 12);
}
} if (i == 1) {
String[] contents = {"1", "汐海", "24", "Java编程", "后端工程师"};
for (int j = 0; j < tableCells.size(); j++) {
XWPFTableCell cell = tableCells.get(j);
WordUtil.handleCellContent(cell, contents[j], fontFamily, 12);
}
} if (i == 2) {
String[] contents = {"2", "悦彬", "23", "炉石传说", "自由职业者"};
for (int j = 0; j < tableCells.size(); j++) {
XWPFTableCell cell = tableCells.get(j);
WordUtil.handleCellContent(cell, contents[j], fontFamily, 12);
}
} if (i == 3) {
WordUtil.handleCellHeight(row, 4540);
XWPFTableCell cell = row.getCell(0);
String imageUrl = "https://b0.bdstatic.com/0e4821a924ce3a31d40da2e5265a4cea.jpg@h_1280";
URL url = new URL(imageUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setConnectTimeout(10000); // 超时时间 10 秒
connection.setReadTimeout(10000);
try (InputStream inputStream = connection.getInputStream()) {
// 将输入流内容转为字节数组
byte[] imageBytes = IOUtils.toByteArray(inputStream);
java.awt.image.BufferedImage image = javax.imageio.ImageIO.read(new ByteArrayInputStream(imageBytes));
int imageWidth = image.getWidth();
int imageHeight = image.getHeight();
// 固定目标高度(厘米)
double desiredHeightCm = 11.12;
// 缩放目标宽度(厘米)
double desiredWidthCm = desiredHeightCm * imageWidth / imageHeight;
// 将厘米转换为 EMU int widthEMU = (int) (desiredWidthCm * 360000);
int heightEMU = (int) desiredHeightCm * 360000;
ByteArrayInputStream reusableInputStream = new ByteArrayInputStream(imageBytes);
WordUtil.handleCellImage(cell.addParagraph().createRun(), reusableInputStream, "本人照片.png", widthEMU, heightEMU);
} finally {
connection.disconnect();
}
WordUtil.handleCellStyle(cell, STVerticalJc.CENTER, ParagraphAlignment.CENTER);
}
HashMap<Integer, Integer> map = new HashMap<>();
map.put(0, 1000);
WordUtil.handleCellWidth(row, map);
}
XWPFTableRow row = rows.get(rows.size() - 1);
WordUtil.handleMergeCell(row, 0, rows.size());
@Cleanup ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
WordUtil.exportWord(document, outputStream);
response.setHeader("Content-Disposition", "attachment; filename=" + java.net.URLEncoder.encode("测试报告.docx", "UTF-8"));
response.setContentType("application/octet-stream; charset=UTF-8");
response.getOutputStream().write(outputStream.toByteArray());
}

报告样式

报告
报告