本文目录导读:

我来提供一个完整的Java实现Office转PDF的案例,包括Word、Excel和PowerPoint的转换。
方案选择
主要有以下几种方案:
- Apache POI + OpenOffice/LibreOffice - 免费开源
- Aspose - 商业但效果好
- JODConverter - 基于OpenOffice
这里我提供最常用的Apache POI + LibreOffice方案和Aspose方案。
基于LibreOffice(免费开源)
添加依赖
<dependencies>
<!-- Apache POI 用于读取Office文件 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version>
</dependency>
<!-- JODConverter 用于调用LibreOffice -->
<dependency>
<groupId>org.jodconverter</groupId>
<artifactId>jodconverter-local</artifactId>
<version>4.4.6</version>
</dependency>
<dependency>
<groupId>org.jodconverter</groupId>
<artifactId>jodconverter-core</artifactId>
<version>4.4.6</version>
</dependency>
</dependencies>
转换工具类
package com.example.officeconverter;
import org.jodconverter.local.LocalConverter;
import org.jodconverter.local.office.LocalOfficeManager;
import org.jodconverter.core.office.OfficeManager;
import org.jodconverter.core.office.OfficeException;
import org.jodconverter.core.DocumentConverter;
import java.io.File;
import java.util.UUID;
public class OfficeToPdfConverter {
private static OfficeManager officeManager;
static {
// 初始化Office管理器
try {
officeManager = LocalOfficeManager.builder()
.install()
.portNumbers(2002) // 使用固定端口
.officeHome("C:/Program Files/LibreOffice") // LibreOffice安装路径
.build();
officeManager.start();
} catch (OfficeException e) {
e.printStackTrace();
}
}
/**
* 转换Office文件为PDF
* @param sourcePath 源文件路径
* @param targetPath 目标PDF路径
* @return 转换后的PDF文件路径
*/
public static String convertToPdf(String sourcePath, String targetPath) {
try {
File sourceFile = new File(sourcePath);
File targetFile = new File(targetPath);
if (!sourceFile.exists()) {
throw new RuntimeException("源文件不存在: " + sourcePath);
}
// 创建目标目录
if (!targetFile.getParentFile().exists()) {
targetFile.getParentFile().mkdirs();
}
// 转换文档
DocumentConverter converter = LocalConverter.make();
converter.convert(sourceFile).to(targetFile).execute();
return targetPath;
} catch (OfficeException e) {
throw new RuntimeException("Office转PDF失败: " + e.getMessage(), e);
}
}
/**
* 转换并生成随机文件名
*/
public static String convertToPdf(String sourcePath) {
String targetPath = "converted_" + UUID.randomUUID().toString() + ".pdf";
return convertToPdf(sourcePath, targetPath);
}
/**
* 关闭Office管理器
*/
public static void close() {
try {
if (officeManager != null) {
officeManager.stop();
}
} catch (OfficeException e) {
e.printStackTrace();
}
}
}
测试类
package com.example.officeconverter;
public class ConverterTest {
public static void main(String[] args) {
try {
// Word转PDF
String wordPath = "D:\\test\\document.docx";
String wordPdfPath = "D:\\test\\document.pdf";
OfficeToPdfConverter.convertToPdf(wordPath, wordPdfPath);
System.out.println("Word文档转换成功!");
// Excel转PDF
String excelPath = "D:\\test\\spreadsheet.xlsx";
String excelPdfPath = "D:\\test\\spreadsheet.pdf";
OfficeToPdfConverter.convertToPdf(excelPath, excelPdfPath);
System.out.println("Excel表格转换成功!");
// PowerPoint转PDF
String pptPath = "D:\\test\\presentation.pptx";
String pptPdfPath = "D:\\test\\presentation.pdf";
OfficeToPdfConverter.convertToPdf(pptPath, pptPdfPath);
System.out.println("PowerPoint转换成功!");
} catch (Exception e) {
e.printStackTrace();
} finally {
// 最后关闭Office管理器
OfficeToPdfConverter.close();
}
}
}
基于Aspose(商业方案)
添加依赖
<dependencies>
<!-- Aspose Words for Word -->
<dependency>
<groupId>com.aspose</groupId>
<artifactId>aspose-words</artifactId>
<version>22.10</version>
<classifier>jdk11</classifier>
</dependency>
<!-- Aspose Cells for Excel -->
<dependency>
<groupId>com.aspose</groupId>
<artifactId>aspose-cells</artifactId>
<version>22.10</version>
</dependency>
<!-- Aspose Slides for PowerPoint -->
<dependency>
<groupId>com.aspose</groupId>
<artifactId>aspose-slides</artifactId>
<version>22.10</version>
<classifier>jdk16</classifier>
</dependency>
</dependencies>
具体实现
package com.example.asposeconverter;
import com.aspose.cells.Workbook;
import com.aspose.slides.Presentation;
import com.aspose.words.Document;
import com.aspose.words.SaveFormat;
public class AsposeConverter {
// 需要设置许可证,否则会有水印
static {
try {
// 设置许可证(如有)
// License license = new License();
// license.setLicense("Aspose.Total.Java.lic");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Word转PDF
*/
public static void wordToPdf(String sourcePath, String targetPath) throws Exception {
Document doc = new Document(sourcePath);
doc.save(targetPath, SaveFormat.PDF);
}
/**
* Excel转PDF
*/
public static void excelToPdf(String sourcePath, String targetPath) throws Exception {
Workbook workbook = new Workbook(sourcePath);
workbook.save(targetPath, com.aspose.cells.SaveFormat.PDF);
}
/**
* PowerPoint转PDF
*/
public static void pptToPdf(String sourcePath, String targetPath) throws Exception {
Presentation pres = new Presentation(sourcePath);
try {
pres.save(targetPath, com.aspose.slides.SaveFormat.Pdf);
} finally {
if (pres != null) {
pres.dispose();
}
}
}
/**
* 根据文件类型自动转换
*/
public static void convertToPdf(String sourcePath, String targetPath) throws Exception {
String extension = sourcePath.substring(sourcePath.lastIndexOf(".")).toLowerCase();
switch (extension) {
case ".doc":
case ".docx":
wordToPdf(sourcePath, targetPath);
break;
case ".xls":
case ".xlsx":
excelToPdf(sourcePath, targetPath);
break;
case ".ppt":
case ".pptx":
pptToPdf(sourcePath, targetPath);
break;
default:
throw new IllegalArgumentException("不支持的文件格式: " + extension);
}
}
}
测试类
package com.example.asposeconverter;
public class AsposeTest {
public static void main(String[] args) {
try {
// Word转PDF
AsposeConverter.convertToPdf(
"D:\\test\\report.docx",
"D:\\test\\report.pdf"
);
System.out.println("Word转换成功");
// Excel转PDF
AsposeConverter.convertToPdf(
"D:\\test\\data.xlsx",
"D:\\test\\data.pdf"
);
System.out.println("Excel转换成功");
// PPT转PDF
AsposeConverter.convertToPdf(
"D:\\test\\slides.pptx",
"D:\\test\\slides.pdf"
);
System.out.println("PPT转换成功");
} catch (Exception e) {
e.printStackTrace();
}
}
}
Web服务实现(Spring Boot)
package com.example.controller;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
@RestController
@RequestMapping("/api/converter")
public class FileConverterController {
private static final String UPLOAD_DIR = "D:/temp/upload/";
private static final String CONVERTED_DIR = "D:/temp/converted/";
/**
* 上传并转换Office文件
*/
@PostMapping("/toPdf")
public ResponseEntity<?> convertToPdf(@RequestParam("file") MultipartFile file) {
try {
// 创建临时目录
Files.createDirectories(Paths.get(UPLOAD_DIR));
Files.createDirectories(Paths.get(CONVERTED_DIR));
// 保存上传的文件
String originalFilename = file.getOriginalFilename();
String extension = getExtension(originalFilename);
String fileName = UUID.randomUUID() + extension;
String uploadPath = UPLOAD_DIR + fileName;
File uploadedFile = new File(uploadPath);
file.transferTo(uploadedFile);
// 转换文件
String pdfFileName = UUID.randomUUID() + ".pdf";
String pdfPath = CONVERTED_DIR + pdfFileName;
OfficeToPdfConverter.convertToPdf(uploadPath, pdfPath);
// 返回下载链接
String downloadUrl = "/api/converter/download/" + pdfFileName;
// 清理上传文件
uploadedFile.delete();
return ResponseEntity.ok()
.body(java.util.Map.of(
"status", "success",
"message", "转换成功",
"downloadUrl", downloadUrl
));
} catch (Exception e) {
return ResponseEntity.badRequest()
.body(java.util.Map.of(
"status", "error",
"message", "转换失败: " + e.getMessage()
));
}
}
/**
* 下载转换后的PDF
*/
@GetMapping("/download/{filename}")
public ResponseEntity<byte[]> downloadFile(@PathVariable String filename) {
try {
Path filePath = Paths.get(CONVERTED_DIR + filename);
if (Files.exists(filePath)) {
byte[] content = Files.readAllBytes(filePath);
return ResponseEntity.ok()
.header("Content-Type", "application/pdf")
.header("Content-Disposition", "attachment; filename=\"" + filename + "\"")
.body(content);
} else {
return ResponseEntity.notFound().build();
}
} catch (IOException e) {
return ResponseEntity.status(500).build();
}
}
private String getExtension(String filename) {
return filename.substring(filename.lastIndexOf(".")).toLowerCase();
}
}
性能优化建议
public class ConverterOptimizer {
// 1. 使用线程池处理并发转换
private static ExecutorService executor = Executors.newFixedThreadPool(10);
// 2. 异步转换
public static CompletableFuture<String> asyncConvert(String sourcePath, String targetPath) {
return CompletableFuture.supplyAsync(() -> {
try {
OfficeToPdfConverter.convertToPdf(sourcePath, targetPath);
return targetPath;
} catch (Exception e) {
throw new RuntimeException(e);
}
}, executor);
}
// 3. 批量转换
public static List<String> batchConvert(List<String> sourcePaths, String targetDir) {
List<CompletableFuture<String>> futures = new ArrayList<>();
for (String sourcePath : sourcePaths) {
String targetPath = targetDir + UUID.randomUUID() + ".pdf";
futures.add(asyncConvert(sourcePath, targetPath));
}
// 等待所有转换完成
return futures.stream()
.map(CompletableFuture::join)
.collect(Collectors.toList());
}
// 4. 文件清理
public static void cleanupOldFiles(String dir, long maxAgeInMillis) {
File directory = new File(dir);
File[] files = directory.listFiles();
if (files != null) {
long now = System.currentTimeMillis();
for (File file : files) {
if (now - file.lastModified() > maxAgeInMillis) {
file.delete();
}
}
}
}
}
注意事项
-
LibreOffice方案:
- 需要安装LibreOffice
- 转换质量取决于LibreOffice的兼容性
- 适合免费开源项目
-
Aspose方案:
- 效果最好,格式兼容性最高
- 需要购买许可证
- 适合企业级应用
-
通用建议:
- 处理好异常和日志
- 考虑内存限制
- 实现文件清理机制
- 注意并发和资源管理
选择哪种方案取决于你的具体需求、预算和使用场景,如果需要更详细的实现或特定功能的扩展,请告诉我。