最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot三步將Word轉為PDF的實戰(zhàn)指南

 更新時間:2025年12月30日 14:27:11   作者:悟空碼字  
這篇文章主要為大家詳細介紹了SpringBoot如何三步實現(xiàn)將Word轉為PDF的相關知識,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下

Word到PDF的奇幻之旅

Word文檔就像個穿著睡衣在家辦公的程序員——舒服但有點隨意。而PDF呢?就是穿上西裝打上領帶,準備去參加董事會的同一人——專業(yè)且紋絲不動!

這轉變過程好比:

  • Word文檔:“哈!我的字體可以隨便換,邊距可以隨意調,圖片還能拖來拖去~”
  • PDF:“閉嘴!現(xiàn)在開始我說了算,每個像素都給我站好崗!”

SpringBoot實現(xiàn)這個轉換,就像是請了個文檔變形金剛,把自由散漫的Word馴化成紀律嚴明的PDF士兵。下面就讓我?guī)阋娮C這場“格式馴化儀式”!

準備階段:裝備你的“變形工具箱”

第一步:Maven依賴大采購

<!-- pom.xml 里加入這些法寶 -->
<dependencies>
    <!-- SpringBoot標準裝備 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <!-- Apache POI - Word文檔的“讀心術” -->
    <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>
    
    <!-- OpenPDF - PDF的“打印機” -->
    <dependency>
        <groupId>com.github.librepdf</groupId>
        <artifactId>openpdf</artifactId>
        <version>1.3.30</version>
    </dependency>
    
    <!-- 文件類型檢測 - 避免把圖片當Word處理 -->
    <dependency>
        <groupId>org.apache.tika</groupId>
        <artifactId>tika-core</artifactId>
        <version>2.7.0</version>
    </dependency>
</dependencies>

第二步:配置屬性文件

# application.yml
word-to-pdf:
  upload-dir: "uploads/"  # Word文檔臨時停靠站
  output-dir: "pdf-output/"  # PDF成品倉庫
  max-file-size: 10MB  # 別想用《戰(zhàn)爭與和平》來考驗我
  
spring:
  servlet:
    multipart:
      max-file-size: 10MB
      max-request-size: 10MB

核心代碼:變身吧,Word君!

1. 文件上傳控制器(接待員)

import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;

@RestController
@RequestMapping("/api/doc-transform")
public class WordToPdfController {
    
    @PostMapping("/word-to-pdf")
    public void convertWordToPdf(
            @RequestParam("file") MultipartFile wordFile,
            HttpServletResponse response) throws IOException {
        
        // 1. 檢查文件:別想用貓咪圖片冒充Word文檔!
        if (!isWordDocument(wordFile)) {
            response.getWriter().write("喂!這不是Word文檔,別騙我!");
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            return;
        }
        
        // 2. 臨時存放Word文件(像安檢前的暫存)
        File tempWordFile = new File("temp_" + System.currentTimeMillis() + ".docx");
        wordFile.transferTo(tempWordFile);
        
        // 3. 開始變形!
        byte[] pdfBytes = WordToPdfConverter.convert(tempWordFile);
        
        // 4. 清理現(xiàn)場(像用完的變形金剛恢復原狀)
        tempWordFile.delete();
        
        // 5. 把PDF交給用戶
        response.setContentType("application/pdf");
        response.setHeader("Content-Disposition", 
            "attachment; filename=\"" + 
            wordFile.getOriginalFilename().replace(".docx", ".pdf") + "\"");
        response.getOutputStream().write(pdfBytes);
        
        System.out.println("轉換成功!又一個Word被成功馴化成PDF!");
    }
    
    private boolean isWordDocument(MultipartFile file) {
        String fileName = file.getOriginalFilename().toLowerCase();
        return fileName.endsWith(".docx") || fileName.endsWith(".doc");
    }
}

2. 轉換器核心(真正的變形引擎)

import org.apache.poi.xwpf.usermodel.*;
import com.lowagie.text.*;
import com.lowagie.text.pdf.PdfWriter;
import java.io.*;

@Component
public class WordToPdfConverter {
    
    public static byte[] convert(File wordFile) throws IOException {
        ByteArrayOutputStream pdfOutputStream = new ByteArrayOutputStream();
        
        try (FileInputStream fis = new FileInputStream(wordFile)) {
            // 1. 打開Word文檔(像打開潘多拉魔盒)
            XWPFDocument document = new XWPFDocument(fis);
            
            // 2. 創(chuàng)建PDF文檔(準備新家)
            Document pdfDocument = new Document();
            PdfWriter.getInstance(pdfDocument, pdfOutputStream);
            pdfDocument.open();
            
            // 3. 逐段搬運內容(像螞蟻搬家)
            System.out.println("開始搬運段落,共" + document.getParagraphs().size() + "段...");
            
            for (XWPFParagraph para : document.getParagraphs()) {
                if (para.getText().trim().isEmpty()) continue;
                
                // 處理文本樣式
                Font font = new Font();
                if (para.getStyle() != null) {
                    switch (para.getStyle()) {
                        case "Heading1":
                            font = new Font(Font.HELVETICA, 18, Font.BOLD);
                            break;
                        case "Heading2":
                            font = new Font(Font.HELVETICA, 16, Font.BOLD);
                            break;
                        default:
                            font = new Font(Font.HELVETICA, 12, Font.NORMAL);
                    }
                }
                
                Paragraph pdfPara = new Paragraph(para.getText(), font);
                pdfDocument.add(pdfPara);
                pdfDocument.add(Chunk.NEWLINE);  // 加個換行,喘口氣
            }
            
            // 4. 處理圖片(最難搬家的部分)
            System.out.println("開始處理圖片,共" + document.getAllPictures().size() + "張...");
            for (XWPFPictureData picture : document.getAllPictures()) {
                try {
                    byte[] pictureData = picture.getData();
                    Image image = Image.getInstance(pictureData);
                    image.scaleToFit(500, 500);  // 給圖片上個緊箍咒,別太大
                    image.setAlignment(Element.ALIGN_CENTER);
                    pdfDocument.add(image);
                    pdfDocument.add(Chunk.NEWLINE);
                } catch (Exception e) {
                    System.err.println("圖片" + picture.getFileName() + "太調皮,轉換失敗: " + e.getMessage());
                }
            }
            
            // 5. 處理表格(Excel表示:我也想來湊熱鬧)
            for (XWPFTable table : document.getTables()) {
                com.lowagie.text.Table pdfTable = 
                    new com.lowagie.text.Table(table.getNumberOfRows());
                
                for (XWPFTableRow row : table.getRows()) {
                    for (XWPFTableCell cell : row.getTableCells()) {
                        pdfTable.addCell(cell.getText());
                    }
                }
                pdfDocument.add(pdfTable);
            }
            
            pdfDocument.close();
            document.close();
            
            System.out.println("轉換完成!生成PDF大小: " + 
                (pdfOutputStream.size() / 1024) + " KB");
            
        } catch (Exception e) {
            System.err.println("轉換過程出現(xiàn)意外: " + e.getMessage());
            throw new IOException("轉換失敗,Word文檔可能被施了魔法", e);
        }
        
        return pdfOutputStream.toByteArray();
    }
}

3. 異常處理(變形失敗的救護車)

@ControllerAdvice
public class DocumentConversionExceptionHandler {
    
    @ExceptionHandler(IOException.class)
    public ResponseEntity<String> handleIOException(IOException e) {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
            .body("文檔轉換失敗,可能原因:\n" +
                  "1. Word文檔被外星人加密了\n" +
                  "2. 文件太大,服務器舉不動了\n" +
                  "3. 網(wǎng)絡連接在打瞌睡\n" +
                  "錯誤詳情: " + e.getMessage());
    }
    
    @ExceptionHandler(InvalidFormatException.class)
    public ResponseEntity<String> handleInvalidFormat(Exception e) {
        return ResponseEntity.badRequest()
            .body("喂!你上傳的是Word文檔嗎?\n" +
                  "我猜你上傳的是:\n" +
                  "□ 貓咪圖片 \n" +
                  "□ Excel表格 \n" +
                  "□ 心靈雞湯文本 \n" +
                  "請上傳正經的.docx或.doc文件!");
    }
}

4. 進度監(jiān)控(變形過程直播)

@Component
public class ConversionProgressService {
    
    private Map<String, Integer> progressMap = new ConcurrentHashMap<>();
    
    public void startConversion(String fileId) {
        progressMap.put(fileId, 0);
        System.out.println("開始轉換文件: " + fileId);
    }
    
    public void updateProgress(String fileId, int percent) {
        progressMap.put(fileId, percent);
        
        // 打印進度條(假裝很高級)
        StringBuilder progressBar = new StringBuilder("[");
        for (int i = 0; i < 20; i++) {
            progressBar.append(i * 5 < percent ? "█" : "?");
        }
        progressBar.append("] ").append(percent).append("%");
        
        System.out.println(fileId + " 轉換進度: " + progressBar.toString());
        
        // 說點騷話鼓勵一下
        if (percent == 50) {
            System.out.println("轉換過半,堅持住!");
        } else if (percent == 90) {
            System.out.println("馬上完成,準備發(fā)射PDF!");
        }
    }
    
    public void completeConversion(String fileId) {
        progressMap.remove(fileId);
        System.out.println(fileId + " 轉換完成,深藏功與名~");
    }
}

前端調用示例(用戶操作界面)

<!DOCTYPE html>
<html>
<head>
    <title>Word轉PDF變形工坊</title>
    <style>
        body { font-family: 'Comic Sans MS', cursive; padding: 20px; }
        .container { max-width: 600px; margin: 0 auto; }
        .drop-zone {
            border: 3px dashed #4CAF50;
            border-radius: 10px;
            padding: 40px;
            text-align: center;
            background: #f9f9f9;
            cursor: pointer;
        }
        .drop-zone:hover { background: #e8f5e9; }
        .convert-btn {
            background: linear-gradient(45deg, #FF6B6B, #4ECDC4);
            color: white;
            border: none;
            padding: 15px 30px;
            border-radius: 25px;
            font-size: 18px;
            cursor: pointer;
            margin-top: 20px;
        }
        .progress-bar {
            width: 100%;
            height: 20px;
            background: #ddd;
            border-radius: 10px;
            margin-top: 20px;
            overflow: hidden;
            display: none;
        }
        .progress-fill {
            height: 100%;
            background: linear-gradient(90deg, #4CAF50, #8BC34A);
            width: 0%;
            transition: width 0.3s;
        }
    </style>
</head>
<body>
    <div class="container">
        <h1>Word轉PDF變形工坊</h1>
        <p>把你的Word文檔扔進來,還你一個乖巧的PDF!</p>
        
        <div class="drop-zone" id="dropZone">
            <h2>拖拽Word文件到這里</h2>
            <p>或者 <label style="color: #2196F3; cursor: pointer;">點擊選擇文件
                <input type="file" id="fileInput" accept=".docx,.doc" hidden>
            </label></p>
        </div>
        
        <button class="convert-btn" onclick="convertToPdf()">
            開始變形!
        </button>
        
        <div class="progress-bar" id="progressBar">
            <div class="progress-fill" id="progressFill"></div>
        </div>
        
        <div id="status" style="margin-top: 20px;"></div>
    </div>

    <script>
        const dropZone = document.getElementById('dropZone');
        const fileInput = document.getElementById('fileInput');
        let selectedFile = null;
        
        // 拖拽功能
        dropZone.addEventListener('dragover', (e) => {
            e.preventDefault();
            dropZone.style.background = '#e8f5e9';
        });
        
        dropZone.addEventListener('drop', (e) => {
            e.preventDefault();
            dropZone.style.background = '#f9f9f9';
            selectedFile = e.dataTransfer.files[0];
            document.getElementById('status').innerHTML = 
                `已選擇: <strong>${selectedFile.name}</strong>`;
        });
        
        fileInput.addEventListener('change', (e) => {
            selectedFile = e.target.files[0];
            document.getElementById('status').innerHTML = 
                `已選擇: <strong>${selectedFile.name}</strong>`;
        });
        
        // 轉換函數(shù)
        async function convertToPdf() {
            if (!selectedFile) {
                alert('請先選擇一個Word文件!');
                return;
            }
            
            const formData = new FormData();
            formData.append('file', selectedFile);
            
            // 顯示進度條
            const progressBar = document.getElementById('progressBar');
            const progressFill = document.getElementById('progressFill');
            progressBar.style.display = 'block';
            
            // 模擬進度(實際項目可以用WebSocket)
            let progress = 0;
            const interval = setInterval(() => {
                progress += 10;
                progressFill.style.width = `${progress}%`;
                if (progress >= 90) clearInterval(interval);
            }, 300);
            
            try {
                const response = await fetch('/api/doc-transform/word-to-pdf', {
                    method: 'POST',
                    body: formData
                });
                
                clearInterval(interval);
                progressFill.style.width = '100%';
                
                if (response.ok) {
                    // 下載PDF
                    const blob = await response.blob();
                    const url = window.URL.createObjectURL(blob);
                    const a = document.createElement('a');
                    a.href = url;
                    a.download = selectedFile.name.replace(/\.docx?$/i, '.pdf');
                    document.body.appendChild(a);
                    a.click();
                    a.remove();
                    
                    document.getElementById('status').innerHTML = 
                        '轉換成功!PDF已開始下載~';
                    
                    // 3秒后重置
                    setTimeout(() => {
                        progressBar.style.display = 'none';
                        progressFill.style.width = '0%';
                        document.getElementById('status').innerHTML = '';
                    }, 3000);
                } else {
                    const errorText = await response.text();
                    document.getElementById('status').innerHTML = 
                        `轉換失敗: ${errorText}`;
                }
            } catch (error) {
                document.getElementById('status').innerHTML = 
                    `網(wǎng)絡錯誤: ${error.message}`;
            }
        }
    </script>
</body>
</html>

高級功能擴展

批量轉換(群變模式)

@Service
public class BatchConversionService {
    
    @Async  // 異步處理,不卡界面
    public CompletableFuture<List<File>> convertMultiple(List<MultipartFile> files) {
        System.out.println("開始批量轉換,共" + files.size() + "個文件,沖鴨!");
        
        List<File> pdfFiles = new ArrayList<>();
        List<CompletableFuture<File>> futures = new ArrayList<>();
        
        for (int i = 0; i < files.size(); i++) {
            final int index = i;
            CompletableFuture<File> future = CompletableFuture.supplyAsync(() -> {
                try {
                    System.out.println("正在轉換第" + (index + 1) + "個文件...");
                    byte[] pdfBytes = WordToPdfConverter.convert(convertToFile(files.get(index)));
                    File pdfFile = new File("converted_" + index + ".pdf");
                    Files.write(pdfFile.toPath(), pdfBytes);
                    return pdfFile;
                } catch (Exception e) {
                    System.err.println("第" + (index + 1) + "個文件轉換失敗: " + e.getMessage());
                    return null;
                }
            });
            futures.add(future);
        }
        
        // 等待所有轉換完成
        CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
        
        for (CompletableFuture<File> future : futures) {
            try {
                File pdf = future.get();
                if (pdf != null) pdfFiles.add(pdf);
            } catch (Exception e) {
                // 忽略失敗的文件
            }
        }
        
        System.out.println("批量轉換完成!成功: " + pdfFiles.size() + 
                          "/" + files.size() + " 個文件");
        return CompletableFuture.completedFuture(pdfFiles);
    }
}

轉換記錄(變形檔案室)

@Entity
@Table(name = "conversion_records")
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ConversionRecord {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String originalFileName;
    private String pdfFileName;
    private Long originalSize;
    private Long pdfSize;
    private LocalDateTime conversionTime;
    private String status;  // SUCCESS, FAILED, PROCESSING
    private String errorMessage;
    
    @PrePersist
    protected void onCreate() {
        conversionTime = LocalDateTime.now();
    }
}

@Repository
public interface ConversionRecordRepository extends JpaRepository<ConversionRecord, Long> {
    List<ConversionRecord> findByStatusOrderByConversionTimeDesc(String status);
}

部署與優(yōu)化建議

1. 性能優(yōu)化

# application.yml 添加
server:
  tomcat:
    max-threads: 200  # 增加線程數(shù)處理并發(fā)轉換
    min-spare-threads: 20

spring:
  task:
    execution:
      pool:
        core-size: 10  # 異步任務線程池
        max-size: 50

2. 內存管理

@Component
public class MemoryWatcher {
    
    @Scheduled(fixedRate = 60000)  // 每分鐘檢查一次
    public void monitorMemory() {
        long usedMemory = Runtime.getRuntime().totalMemory() - 
                         Runtime.getRuntime().freeMemory();
        long maxMemory = Runtime.getRuntime().maxMemory();
        
        double usagePercentage = (double) usedMemory / maxMemory * 100;
        
        if (usagePercentage > 80) {
            System.out.println("內存警告:使用率 " + 
                String.format("%.1f", usagePercentage) + "%");
            // 觸發(fā)垃圾回收
            System.gc();
        }
    }
}

總結:Word轉PDF的奇幻旅程終點站

經過這一番折騰,我們成功打造了一個SpringBoot牌文檔變形金剛!總結一下這場冒險:

我們實現(xiàn)了什么

  • 格式馴化:把自由的Word變成規(guī)矩的PDF
  • 異步處理:大文件轉換不卡界面
  • 進度監(jiān)控:實時查看轉換進度
  • 錯誤處理:優(yōu)雅處理各種意外情況
  • 批量操作:一次性馴化整個Word文檔家族

注意事項

  • 字體問題:有些特殊字體PDF可能不認識,需要額外處理
  • 復雜格式:Word里的高級排版(如文本框、藝術字)可能變形
  • 內存消耗:大文檔轉換時注意內存溢出
  • 并發(fā)限制:同時轉換太多文檔可能導致服務器喘不過氣

Word轉PDF就像給文檔穿上“防改鎧甲”,SpringBoot就是我們打造這副鎧甲的智能工廠。雖然過程中會遇到各種奇葩格式的“刺頭文檔”,但只要有耐心調試,最終都能把它們治理得服服帖帖!

到此這篇關于SpringBoot三步將Word轉為PDF的實戰(zhàn)指南的文章就介紹到這了,更多相關SpringBoot Word轉PDF內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java冒泡排序及優(yōu)化介紹

    Java冒泡排序及優(yōu)化介紹

    大家好,本篇文章主要講的是Java冒泡排序及優(yōu)化介紹,感興趣的同學趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2021-12-12
  • Java?Post請求發(fā)送form-data表單參數(shù)詳細示例代碼

    Java?Post請求發(fā)送form-data表單參數(shù)詳細示例代碼

    POST請求是一種常見的網(wǎng)絡通信操作,用于向服務器發(fā)送數(shù)據(jù),這種請求通常用于上傳文件或者提交包含大量數(shù)據(jù)的表單,這篇文章主要介紹了Java?Post請求發(fā)送form-data表單參數(shù)的相關資料,需要的朋友可以參考下
    2025-07-07
  • MyBatis使用接口映射的方法步驟

    MyBatis使用接口映射的方法步驟

    映射器是MyBatis中最核心的組件之一,本文主要介紹了MyBatis使用接口映射的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2024-07-07
  • Java Swing組件BoxLayout布局用法示例

    Java Swing組件BoxLayout布局用法示例

    這篇文章主要介紹了Java Swing組件BoxLayout布局用法,結合實例形式分析了Swing使用BoxLayout容器進行布局的相關方法與操作技巧,需要的朋友可以參考下
    2017-11-11
  • Java雙重檢查加鎖單例模式的詳解

    Java雙重檢查加鎖單例模式的詳解

    今天小編就為大家分享一篇關于Java雙重檢查加鎖單例模式的詳解,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-03-03
  • Spring?Boot?整合?Fisco?Bcos的案例分析(區(qū)塊鏈)

    Spring?Boot?整合?Fisco?Bcos的案例分析(區(qū)塊鏈)

    本篇文章介紹的?Spring?Boot?整合?Fisco?Bcos的案例,是在阿里云服務器上部署驗證的。大家可根據(jù)自己的電腦環(huán)境,對比該案例進行開發(fā)即可,具體案例代碼跟隨小編一起看看吧
    2022-01-01
  • 簡單的一次springMVC路由跳轉實現(xiàn)

    簡單的一次springMVC路由跳轉實現(xiàn)

    本文主要介紹了springMVC路由跳轉實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-04-04
  • SpringBoot整合SQLite詳細過程

    SpringBoot整合SQLite詳細過程

    SQLite是一個不需要服務、不需要配置、不需要外部依賴的開源SQL輕量級數(shù)據(jù)庫,本文給大家介紹SpringBoot整合SQLite詳細過程,感興趣的朋友一起看看吧
    2025-06-06
  • Java反射機制深入理解

    Java反射機制深入理解

    這篇文章主要介紹了Java反射機制深入理解的相關資料,希望通過本文大家能理解這部分內容,需要的朋友可以參考下
    2017-09-09
  • Java Kafka實現(xiàn)延遲隊列的示例代碼

    Java Kafka實現(xiàn)延遲隊列的示例代碼

    kafka作為一個使用廣泛的消息隊列,很多人都不會陌生。本文將利用Kafka實現(xiàn)延遲隊列,文中的示例代碼講解詳細,感興趣的小伙伴可以嘗試一下
    2022-08-08

最新評論

绥芬河市| 平利县| 云霄县| 逊克县| 若羌县| 兰西县| 佳木斯市| 监利县| 临泽县| 葫芦岛市| 赤峰市| 沂源县| 十堰市| 广汉市| 嘉黎县| 泸水县| 玉门市| 武穴市| 洛宁县| 宜都市| 鄱阳县| 宜章县| 化州市| 邹平县| 湘潭县| 新营市| 江华| 治多县| 新竹县| 韩城市| 措美县| 从江县| 镇安县| 通化市| 大竹县| 来安县| 肇源县| 武清区| 阳春市| 休宁县| 沂源县|