大家好,我是小悟。
第一章:Word和PDF的“爱恨情仇”
1.1 初遇:两种不同的“性格”
Word同学:活泼开朗的编辑小王子,天生爱打扮(格式丰富),随时可以改头换面(可编辑),但有时候过于花哨,换个环境就“水土不服”。
PDF同学:高冷严谨的档案管理员,保持原样绝不动摇(格式固定),去哪都一个样(跨平台一致),但有点固执——“你可以看我,但别想碰我”(难以编辑)。
1.2 办公室恋情发展
- Word转PDF:Word决定“从良”,放弃花哨生活,变成稳重可靠的PDF。这叫“爱的封印”——把美好定格,防止别人乱改。
- PDF转Word:PDF想要“放开自我”,尝试可编辑的生活。但这个过程就像“开盲盒”——有时候能完美转换,有时候……只能说“尽力了”。
第二章:SpringBoot当“红娘”的准备步骤
2.1 创建SpringBoot“婚介所”
# 用Spring Initializr创建项目,记得带上这些“彩礼”: - Spring Web (提供REST API) - Apache POI (处理Word) - PDFBox (处理PDF) - OpenPDF (或iText,用于PDF生成)2.2 Maven依赖配置(pom.xml)
<!-- 婚礼请柬列表 --> <dependencies> <!-- SpringBoot基础套餐 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Word处理专家:Apache POI全家桶 --> <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> <!-- PDF处理专家:PDFBox --> <dependency> <groupId>org.apache.pdfbox</groupId> <artifactId>pdfbox</artifactId> <version>2.0.27</version> </dependency> <!-- 另一种PDF生成选择:OpenPDF --> <dependency> <groupId>com.github.librepdf</groupId> <artifactId>openpdf</artifactId> <version>1.3.30</version> </dependency> </dependencies>第三章:详细实现步骤(带完整代码)
3.1 Word转PDF:Word的“成熟仪式”
3.1.1 创建Word处理服务
import org.apache.poi.xwpf.usermodel.*; import org.springframework.stereotype.Service; import org.apache.pdfbox.pdmodel.*; import org.apache.pdfbox.pdmodel.font.PDType1Font; import com.lowagie.text.*; import com.lowagie.text.pdf.PdfWriter; import java.io.*; @Service public class WordToPdfService { /** * 方法1:使用Apache POI + OpenPDF(适合.doc和.docx) * 这是我们的“豪华版”转换,带格式的 */ public void convertWordToPdf(InputStream wordStream, OutputStream pdfStream) throws Exception { // 1. 读取Word文档(就像给Word做体检) XWPFDocument document = new XWPFDocument(wordStream); // 2. 创建PDF文档(准备新衣服) Document pdfDocument = new Document(); PdfWriter.getInstance(pdfDocument, pdfStream); pdfDocument.open(); // 3. 逐段处理(把Word的每一句话翻译成PDF能听懂的语言) for (XWPFParagraph paragraph : document.getParagraphs()) { String text = paragraph.getText(); if (!text.isEmpty()) { // 设置字体大小(PDF比较挑剔,要明确告诉它) Font font = new Font(Font.HELVETICA, 12); pdfDocument.add(new Paragraph(text, font)); } } // 4. 处理表格(如果有的话) for (XWPFTable table : document.getTables()) { // 这里可以添加表格处理逻辑 // 为了简化,我们先跳过复杂的表格转换 } // 5. 完成转换(礼成!) pdfDocument.close(); document.close(); } /** * 方法2:快速转换版(只提取文本,适合简单文档) * 这是“经济适用型”转换 */ public void convertWordToPdfSimple(File wordFile, File pdfFile) throws IOException { XWPFDocument doc = new XWPFDocument(new FileInputStream(wordFile)); try (PDDocument pdfDoc = new PDDocument()) { PDPage page = new PDPage(); pdfDoc.addPage(page); try (PDPageContentStream contentStream = new PDPageContentStream(pdfDoc, page)) { contentStream.beginText(); contentStream.setFont(PDType1Font.HELVETICA, 12); contentStream.newLineAtOffset(50, 700); // 逐段添加文本 for (XWPFParagraph para : doc.getParagraphs()) { String text = para.getText(); if (!text.trim().isEmpty()) { contentStream.showText(text); contentStream.newLineAtOffset(0, -15); // 换行 } } contentStream.endText(); } pdfDoc.save(pdfFile); } doc.close(); } }3.1.2 创建REST控制器
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.*; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.*; @RestController @RequestMapping("/api/document") public class DocumentConversionController { @Autowired private WordToPdfService wordToPdfService; /** * Word转PDF的API端点 * 访问方式:POST /api/document/word-to-pdf */ @PostMapping("/word-to-pdf") public ResponseEntity<byte[]> convertWordToPdf( @RequestParam("file") MultipartFile file) { try { // 1. 检查文件类型(确保不是乱来的文件) String filename = file.getOriginalFilename(); if (filename == null || (!filename.endsWith(".docx") && !filename.endsWith(".doc"))) { return ResponseEntity.badRequest() .body("只能上传.doc或.docx文件!".getBytes()); } // 2. 创建临时文件(给Word和PDF准备临时婚房) File tempWordFile = File.createTempFile("temp", ".docx"); File tempPdfFile = File.createTempFile("converted", ".pdf"); // 3. 保存上传的文件 file.transferTo(tempWordFile); // 4. 执行转换(见证奇迹的时刻!) wordToPdfService.convertWordToPdfSimple( tempWordFile, tempPdfFile); // 5. 读取生成的PDF byte[] pdfBytes = Files.readAllBytes(tempPdfFile.toPath()); // 6. 设置响应头(告诉浏览器:这是PDF,请用PDF方式打开) HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_PDF); headers.setContentDisposition( ContentDisposition.attachment() .filename(filename.replace(".docx", ".pdf").replace(".doc", ".pdf")) .build() ); // 7. 清理临时文件(婚房不能留着,要环保) tempWordFile.delete(); tempPdfFile.delete(); return new ResponseEntity<>(pdfBytes, headers, HttpStatus.OK); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(("转换失败:" + e.getMessage()).getBytes()); } } }3.2 PDF转Word:PDF的“解放运动”
3.2.1 创建PDF转Word服务
import org.apache.pdfbox.pdmodel.PDDocument; import org.apache.pdfbox.text.PDFTextStripper; import org.apache.poi.xwpf.usermodel.*; import org.springframework.stereotype.Service; import java.io.*; @Service public class PdfToWordService { /** * PDF转Word - 文本提取版 * 警告:这就像把煎蛋变回鸡蛋——能变,但样子不一样了 */ public void convertPdfToWord(File pdfFile, File wordFile) throws IOException { // 1. 读取PDF(撬开PDF的嘴,让它说话) try (PDDocument document = PDDocument.load(pdfFile)) { // 2. 创建Word文档(准备新容器) XWPFDocument wordDoc = new XWPFDocument(); // 3. 提取PDF文本(让PDF"吐"出文字) PDFTextStripper stripper = new PDFTextStripper(); String text = stripper.getText(document); // 4. 按行分割并添加到Word String[] lines = text.split("\n"); for (String line : lines) { if (!line.trim().isEmpty()) { XWPFParagraph paragraph = wordDoc.createParagraph(); XWPFRun run = paragraph.createRun(); run.setText(line); run.setFontSize(12); } } // 5. 保存Word文档(完成转换) try (FileOutputStream out = new FileOutputStream(wordFile)) { wordDoc.write(out); } wordDoc.close(); } } /** * 高级版:带基本格式保留 * 注意:PDF转Word是"世界难题",不要期待完美 */ public void convertPdfToWordAdvanced(InputStream pdfStream, OutputStream wordStream) throws IOException { try (PDDocument pdfDoc = PDDocument.load(pdfStream); XWPFDocument wordDoc = new XWPFDocument()) { PDFTextStripper stripper = new PDFTextStripper(); // 设置提取参数 stripper.setSortByPosition(true); // 按位置排序 stripper.setStartPage(1); // 从第1页开始 stripper.setEndPage(pdfDoc.getNumberOfPages()); // 到最后一页 String text = stripper.getText(pdfDoc); // 处理文本,尝试保留一些结构 String[] paragraphs = text.split("\n\n"); // 假设空行是段落分隔 for (String paraText : paragraphs) { if (paraText.trim().length() > 0) { XWPFParagraph paragraph = wordDoc.createParagraph(); // 设置段落格式 paragraph.setAlignment(ParagraphAlignment.LEFT); // 添加文本 XWPFRun run = paragraph.createRun(); run.setText(paraText); run.setFontFamily("宋体"); run.setFontSize(12); // 添加空行作为段落间隔 wordDoc.createParagraph(); } } wordDoc.write(wordStream); } } }3.2.2 添加PDF转Word的API端点
// 在DocumentConversionController中添加 @PostMapping("/pdf-to-word") public ResponseEntity<byte[]> convertPdfToWord( @RequestParam("file") MultipartFile file) { try { // 1. 检查文件类型 String filename = file.getOriginalFilename(); if (filename == null || !filename.endsWith(".pdf")) { return ResponseEntity.badRequest() .body("只能上传.pdf文件!".getBytes()); } // 2. 创建临时文件 File tempPdfFile = File.createTempFile("temp", ".pdf"); File tempWordFile = File.createTempFile("converted", ".docx"); // 3. 保存上传的文件 file.transferTo(tempPdfFile); // 4. 执行转换(让PDF"变身") PdfToWordService pdfToWordService = new PdfToWordService(); pdfToWordService.convertPdfToWord(tempPdfFile, tempWordFile); // 5. 读取生成的Word byte[] wordBytes = Files.readAllBytes(tempWordFile.toPath()); // 6. 设置响应头 HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); headers.setContentDisposition( ContentDisposition.attachment() .filename(filename.replace(".pdf", ".docx")) .build() ); // 7. 清理临时文件 tempPdfFile.delete(); tempWordFile.delete(); return new ResponseEntity<>(wordBytes, headers, HttpStatus.OK); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body(("转换失败:" + e.getMessage()).getBytes()); } }3.3 创建前端页面(HTML + JavaScript)
<!DOCTYPE html> <html> <head> <title>文档转换器 - Word和PDF的相亲平台</title> <style> body { font-family: 'Comic Sans MS', cursive, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); } .container { background: white; padding: 30px; border-radius: 15px; box-shadow: 0 10px 30px rgba(0,0,0,0.1); } h1 { color: #2c3e50; text-align: center; margin-bottom: 30px; } .converter-box { border: 2px dashed #3498db; padding: 20px; border-radius: 10px; margin: 20px 0; text-align: center; transition: all 0.3s; } .converter-box:hover { border-color: #e74c3c; background: #f9f9f9; } button { background: #3498db; color: white; border: none; padding: 12px 24px; border-radius: 25px; cursor: pointer; font-size: 16px; margin: 10px; transition: all 0.3s; } button:hover { background: #2980b9; transform: translateY(-2px); } #result { margin-top: 20px; padding: 15px; background: #ecf0f1; border-radius: 5px; display: none; } .tips { background: #fff3cd; border-left: 4px solid #ffc107; padding: 15px; margin: 20px 0; border-radius: 0 5px 5px 0; } </style> </head> <body> <div class="container"> <h1>📄 Word ↔ PDF 转换器 📄</h1> <p>让Word和PDF愉快地"谈恋爱"!选择你想进行的转换:</p> <div class="tips"> <strong>温馨提示:</strong><br> 1. Word转PDF:婚礼很完美,格式基本保留<br> 2. PDF转Word:离婚再结婚,可能损失一些"财产"(格式)<br> 3. 文件大小限制:10MB以内 </div> <!-- Word转PDF区域 --> <div class="converter-box"> <h2>Word → PDF 转换</h2> <p>让活泼的Word变成稳重的PDF(推荐使用)</p> <input type="file" id="wordFile" accept=".doc,.docx"> <button onclick="convertWordToPdf()">开始转换!</button> </div> <!-- PDF转Word区域 --> <div class="converter-box"> <h2>PDF → Word 转换</h2> <p>尝试让高冷的PDF变得可编辑(结果可能"惊喜")</p> <input type="file" id="pdfFile" accept=".pdf"> <button onclick="convertPdfToWord()">勇敢尝试!</button> </div> <!-- 结果显示区域 --> <div id="result"> <h3>转换结果</h3> <p id="resultMessage"></p> <a id="downloadLink" style="display:none;"> <button>下载转换后的文件</button> </a> </div> </div> <script> // Word转PDF函数 async function convertWordToPdf() { const fileInput = document.getElementById('wordFile'); const file = fileInput.files[0]; if (!file) { alert('请先选择一个Word文件!'); return; } const formData = new FormData(); formData.append('file', file); try { const response = await fetch('/api/document/word-to-pdf', { method: 'POST', body: formData }); if (response.ok) { const blob = await response.blob(); const url = window.URL.createObjectURL(blob); document.getElementById('result').style.display = 'block'; document.getElementById('resultMessage').textContent = '转换成功!Word已成功"进化"为PDF!'; const downloadLink = document.getElementById('downloadLink'); downloadLink.href = url; downloadLink.download = file.name.replace(/\.(docx|doc)$/, '.pdf'); downloadLink.style.display = 'block'; } else { throw new Error('转换失败'); } } catch (error) { document.getElementById('result').style.display = 'block'; document.getElementById('resultMessage').textContent = '转换失败:' + error.message; } } // PDF转Word函数 async function convertPdfToWord() { const fileInput = document.getElementById('pdfFile'); const file = fileInput.files[0]; if (!file) { alert('请先选择一个PDF文件!'); return; } const formData = new FormData(); formData.append('file', file); try { const response = await fetch('/api/document/pdf-to-word', { method: 'POST', body: formData }); if (response.ok) { const blob = await response.blob(); const url = window.URL.createObjectURL(blob); document.getElementById('result').style.display = 'block'; document.getElementById('resultMessage').textContent = 'PDF成功"解放"为Word!部分格式可能丢失,请理解。'; const downloadLink = document.getElementById('downloadLink'); downloadLink.href = url; downloadLink.download = file.name.replace('.pdf', '.docx'); downloadLink.style.display = 'block'; } else { throw new Error('转换失败'); } } catch (error) { document.getElementById('result').style.display = 'block'; document.getElementById('resultMessage').textContent = '转换失败:' + error.message + '。PDF可能太"固执"了!'; } } </script> </body> </html>3.4 添加Application主类
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; import org.springframework.web.multipart.commons.CommonsMultipartResolver; @SpringBootApplication public class DocumentConverterApplication { public static void main(String[] args) { SpringApplication.run(DocumentConverterApplication.class, args); System.out.println("======================================"); System.out.println("文档转换服务启动成功!"); System.out.println("访问地址:http://localhost:8080"); System.out.println("Word和PDF可以开始'谈恋爱'了!"); System.out.println("======================================"); } @Bean(name = "multipartResolver") public CommonsMultipartResolver multipartResolver() { CommonsMultipartResolver resolver = new CommonsMultipartResolver(); resolver.setMaxUploadSize(10485760); // 10MB限制 resolver.setDefaultEncoding("UTF-8"); return resolver; } }3.5 添加配置文件(application.yml)
spring: servlet: multipart: max-file-size: 10MB max-request-size: 10MB application: name: document-converter server: port: 8080 servlet: context-path: / # 日志配置,方便调试 logging: level: org.springframework.web: INFO com.example.documentconverter: DEBUG file: name: logs/document-converter.log第四章:项目优化和高级功能
4.1 添加批量转换功能
@Service public class BatchConversionService { /** * 批量转换 - 适合大量文档处理 * 比如:把整个部门的Word报告都转成PDF */ public void batchWordToPdf(List<File> wordFiles, File outputDir) { wordFiles.parallelStream().forEach(wordFile -> { try { File pdfFile = new File(outputDir, wordFile.getName().replaceFirst("\\.[^.]+$", "") + ".pdf"); // 调用转换服务 // wordToPdfService.convertWordToPdfSimple(wordFile, pdfFile); System.out.println("转换完成: " + wordFile.getName()); } catch (Exception e) { System.err.println("转换失败: " + wordFile.getName() + " - " + e.getMessage()); } }); } }4.2 添加转换进度跟踪
@Component public class ConversionProgressTracker { private Map<String, ConversionStatus> statusMap = new ConcurrentHashMap<>(); public enum ConversionStatus { PENDING, PROCESSING, COMPLETED, FAILED } public void startConversion(String taskId, String filename) { statusMap.put(taskId, ConversionStatus.PROCESSING); } public void updateProgress(String taskId, int progress) { // 更新进度 } public ConversionStatus getStatus(String taskId) { return statusMap.getOrDefault(taskId, ConversionStatus.PENDING); } }4.3 添加水印功能
@Service public class WatermarkService { /** * 给PDF添加水印 */ public void addWatermarkToPdf(File pdfFile, String watermarkText) throws IOException { try (PDDocument document = PDDocument.load(pdfFile)) { for (PDPage page : document.getPages()) { try (PDPageContentStream contentStream = new PDPageContentStream(document, page, PDPageContentStream.AppendMode.APPEND, true)) { // 设置水印样式 contentStream.setFont(PDType1Font.HELVETICA_OBLIQUE, 60); contentStream.setNonStrokingColor(200, 200, 200); // 浅灰色 // 旋转45度 contentStream.beginText(); contentStream.setTextRotation(Math.toRadians(45), page.getMediaBox().getWidth() / 2, page.getMediaBox().getHeight() / 2); contentStream.showText(watermarkText); contentStream.endText(); } } document.save(pdfFile); } } }第五章:总结与心得
5.1 转换效果对比
Word转PDF:
- 成功率高,就像把新鲜水果做成果酱——能很好地保存原味
- 格式基本保留,布局不乱
- 推荐使用Apache POI + OpenPDF组合
PDF转Word:
- 像把炒熟的鸡蛋变回生鸡蛋——技术上有难度
- 复杂格式(表格、特殊排版)容易丢失
- 扫描版PDF需要OCR,这是另一个故事了
5.2 实战建议
- 选择合适的工具库:
- 简单需求:Apache POI + PDFBox
- 复杂需求:考虑Aspose或商业库(但要花钱)
- 云端方案:直接用Microsoft Graph API或Google Docs API
- 性能优化建议:
- 大文件分块处理
- 使用内存映射文件
- 考虑异步处理 + WebSocket推送进度
- 错误处理要点:
- 一定要关闭文档流(否则内存泄漏)
- 添加文件类型验证
- 设置合理的超时时间
- 安全注意事项:
- 限制上传文件大小
- 检查文件内容(防止恶意文件)
- 临时文件及时清理
5.3 结语
通过这个项目,我们成功地为Word和PDF搭建了一个"相亲平台":
- Word转PDF就像一场浪漫的婚礼:Word穿上PDF的婚纱,承诺"从今以后,我的格式永不变心"。
- PDF转Word则像一场冒险:PDF尝试脱下严肃的外套,说"让我也试试自由的感觉"。
谢谢你看我的文章,既然看到这里了,如果觉得不错,随手点个赞、转发、在看三连吧,感谢感谢。那我们,下次再见。
您的一键三连,是我更新的最大动力,谢谢
山水有相逢,来日皆可期,谢谢阅读,我们再会
我手中的金箍棒,上能通天,下能探海