在用户下载Word报告的同时,系统会自动将Word文档转换为高清图片并保存到服务器,方便后续使用。
Word文档 → PDF文档 → PNG图片(每页一张)
转换流程:
1. 用户请求下载Word报告
2. 系统生成Word文档并返回给用户
3. 后台异步处理:
- 将Word文档保存到服务器
- 将Word转换为PDF
- 将PDF的每一页渲染为PNG图片(300 DPI高清)
- 保存图片到服务器
F:\file\estate\reports\
├── {uuid}.docx # Word原文件
└── images\
├── {uuid}_page_1.png # 第1页图片
├── {uuid}_page_2.png # 第2页图片
└── {uuid}_page_N.png # 第N页图片
接口地址: GET /admin/stat/report/fuzzyReport
请求参数:
| 参数名 | 类型 | 必填 | 说明 |
|--------|------|------|------|
| checkTaskId | Integer | 是 | 检查任务ID |
| templateId | Integer | 是 | 报告模板ID |
| uuid | String | 否 | 文件唯一标识,传入则保存文件并转换为图片 |
使用示例:
# 仅下载,不保存
curl "http://localhost:8080/admin/stat/report/fuzzyReport?checkTaskId=1&templateId=1"
# 下载并保存+转图片
curl "http://localhost:8080/admin/stat/report/fuzzyReport?checkTaskId=1&templateId=1&uuid=abc123"
file:
upload:
dir: F:\file\estate # Windows路径
# dir: /data/uploads # Linux路径
实际保存路径:
- Word文件:${file.upload.dir}/reports/
- 图片文件:${file.upload.dir}/reports/images/
<!-- PDF to Image conversion -->
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.29</version>
</dependency>
private void download(XWPFTemplate doc, HttpServletResponse response, String typeName,
Integer checkTaskId, Integer templateId, String uuid) throws IOException {
// ... 生成Word并返回给用户 ...
// 如果传入uuid,则保存文件并转换为图片
if(uuid != null && !uuid.isEmpty()) {
saveFileToServer(docBytes, docName + ".docx", uuid);
convertWordToImagesAndSave(docBytes, uuid); // 转换为图片
}
}
private void convertWordToImagesAndSave(byte[] docBytes, String uuid) {
// 1. Word → PDF
XWPFDocument document = new XWPFDocument(new ByteArrayInputStream(docBytes));
ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream();
PdfConverter.getInstance().convert(document, pdfOutputStream, options);
// 2. PDF → 图片
PDDocument pdfDocument = PDDocument.load(pdfBytes);
PDFRenderer pdfRenderer = new PDFRenderer(pdfDocument);
// 3. 逐页渲染并保存
for (int i = 0; i < pageCount; i++) {
BufferedImage image = pdfRenderer.renderImageWithDPI(i, 300, ImageType.RGB);
ImageIO.write(image, "PNG", imagePath.toFile());
}
}
DPI设置: 300(高清)
- 150 DPI:普通质量,文件较小
- 300 DPI:高清质量,适合打印(当前设置)
- 600 DPI:超高清,文件很大
图片格式: PNG
- 无损压缩
- 支持透明背景
- 适合文档类图片
当前实现是同步的,建议改为异步处理:
@Async
public void convertWordToImagesAsync(byte[] docBytes, String uuid) {
convertWordToImagesAndSave(docBytes, uuid);
}
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean(name = "imageConversionExecutor")
public Executor imageConversionExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(2);
executor.setMaxPoolSize(5);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("image-convert-");
executor.initialize();
return executor;
}
}
对于相同的文档,可以检查是否已存在图片,避免重复转换:
Path imagePath = imageDir.resolve(uuid + "_page_1.png");
if (Files.exists(imagePath)) {
log.info("图片已存在,跳过转换: {}", uuid);
return;
}
try {
convertWordToImagesAndSave(docBytes, uuid);
} catch (Exception e) {
log.error("Word转图片失败: uuid={}", uuid, e);
// 不抛出异常,用户仍可正常下载Word文档
}
@Test
public void testConvertWordToImages() {
// 准备测试数据
byte[] docBytes = Files.readAllBytes(Path.of("test.docx"));
String uuid = UUID.randomUUID().toString();
// 执行转换
reportController.convertWordToImagesAndSave(docBytes, uuid);
// 验证结果
Path imagePath = Path.of(REPORT_UPLOAD_DIR, "images", uuid + "_page_1.png");
assertTrue(Files.exists(imagePath));
}
# 1. 调用接口下载并转换
curl -o test.docx "http://localhost:8080/admin/stat/report/fuzzyReport?checkTaskId=1&templateId=1&uuid=test123"
# 2. 检查文件是否生成
ls F:\file\estate\reports\test123.docx
ls F:\file\estate\reports\images\test123_page_*.png
原因: 系统缺少中文字体
解决方案:bash # Windows: 确保C:\Windows\Fonts目录有中文字体 # Linux: 安装中文字体 sudo apt-get install fonts-wqy-zenhei fonts-wqy-microhei
原因: DPI设置过低
解决方案: 调整DPI参数java BufferedImage image = pdfRenderer.renderImageWithDPI(i, 300, ImageType.RGB); // 改为 600 DPI BufferedImage image = pdfRenderer.renderImageWithDPI(i, 600, ImageType.RGB);
原因: 同步处理阻塞主线程
解决方案: 使用异步处理(见性能优化建议)
原因: 图片文件占用空间大
解决方案:
1. 降低DPI(300→150)
2. 使用JPEG格式(PNG→JPEG)
3. 定期清理旧文件
4. 使用对象存储(OSS)
@GetMapping("/getReportImages")
@ResponseBody
public Result<List<String>> getReportImages(@RequestParam String uuid) {
List<String> imageUrls = new ArrayList<>();
Path imageDir = Path.of(REPORT_UPLOAD_DIR, "images");
try (var stream = Files.list(imageDir)) {
stream.filter(path -> path.getFileName().toString().startsWith(uuid))
.sorted()
.forEach(path -> {
String url = "/files/images/" + path.getFileName().toString();
imageUrls.add(url);
});
}
return Result.success(imageUrls);
}
// 支持JPEG格式(文件更小)
ImageIO.write(image, "JPEG", imagePath.toFile());
// 支持WebP格式(更高压缩率)
// 需要额外依赖
private BufferedImage addWatermark(BufferedImage image, String watermarkText) {
Graphics2D g2d = image.createGraphics();
g2d.setFont(new Font("Arial", Font.BOLD, 50));
g2d.setColor(new Color(255, 0, 0, 50)); // 半透明红色
g2d.drawString(watermarkText, 100, 100);
g2d.dispose();
return image;
}
file:
upload:
dir: F:\file\estate
file:
upload:
dir: /data/uploads
确保目录权限:bash sudo mkdir -p /data/uploads/reports/images sudo chown -R tomcat:tomcat /data/uploads sudo chmod -R 755 /data/uploads
# 挂载数据卷
docker run -v /host/uploads:/data/uploads your-image
建议监控以下指标:
- 转换成功率
- 平均转换时间
- 磁盘空间使用率
- 转换队列长度(异步模式)
v1.0.0 (2026-01-24)
- ✅ 实现Word转图片基础功能
- ✅ 支持多页文档转换
- ✅ 300 DPI高清输出
- ✅ 完善的异常处理和日志记录
- ✅ 资源自动释放
如有问题,请联系开发团队。