首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Java 調用 wkhtmltopdf 實現 HTML 轉 PDF:從命令行到工具類封裝完整指南

     編輯:Java 綜合教學

Java 調用 wkhtmltopdf 實現 HTML 轉 PDF:從命令行到工具類封裝完整指南

在 Java 生態中,iText 雖是 PDF 生成的首選開源庫,但面對數百張業務表單,為每張設計導出模板顯然不現實。wkhtmltopdf 基於 WebKit 渲染引擎,能直接將現成的 HTML 頁面(含 CSS、JS)高保真轉為 PDF,成為「以網頁代模板」的最佳替代方案。本文從原理、安裝、命令行調用、到生產級 Java 封裝類,全鏈路解析落地實踐。

一、 核心原理與安裝

  • 本質:無頭瀏覽器渲染 HTML → 打印為 PDF
  • 官網:wkhtmltopdf.org | GitHub:源碼倉庫
  • 跨平台:Windows / Linux / macOS 均提供預編譯二進制

二、 命令行快速驗證

# Windows 示例
wkhtmltopdf.exe https://www.cnblogs.com D:/cnblogs.pdf

# 指定頁眉、邊距、頁碼
wkhtmltopdf --header-center "報表" --header-line --margin-top 2cm https://example.com report.pdf

三、 Java 進程調用封裝(生產級工具類)

核心思路:Runtime.getRuntime().exec() 啟動進程,必須單獨線程消費 stdout/stderr,否則緩衝區填滿導致進程掛起。

1. 流消費線程

public class StreamGobbler extends Thread {
    private final InputStream is;
    public StreamGobbler(InputStream is) { this.is = is; }
    @Override
    public void run() {
        try (BufferedReader br = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8))) {
            String line;
            while ((line = br.readLine()) != null) {
                log.debug("[wkhtmltopdf] {}", line); // 接入 SLF4J
            }
        } catch (IOException e) {
            log.error("流讀取異常", e);
        }
    }
}

2. 核心轉換類

public class HtmlToPdfConverter {
    // wkhtmltopdf 可執行文件路徑(建議通過配置文件注入)
    private static final String WKHTMLTOPDF_PATH = "D:/wkhtmltopdf/bin/wkhtmltopdf.exe";

    /**
     * HTML 轉 PDF
     * @param srcPath  源 HTML 路徑(支持本地文件、HTTP/HTTPS URL)
     * @param destPath 目標 PDF 保存路徑
     * @return 轉換成功返回 true
     */
    public static boolean convert(String srcPath, String destPath) {
        File destFile = new File(destPath);
        File parentDir = destFile.getParentFile();
        if (!parentDir.exists() && !parentDir.mkdirs()) {
            log.error("無法創建目標目錄: {}", parentDir);
            return false;
        }

        // 構建命令參數
        List cmd = new ArrayList<>();
        cmd.add(WKHTMLTOPDF_PATH);
        cmd.add("--header-line");                    // 頁眉底線
        cmd.add("--header-center"); cmd.add("機密報表"); // 頁眉居中內容
        cmd.add("--header-spacing"); cmd.add("10");    // 頁眉與正文間距
        cmd.add("--footer-right"); cmd.add("[page]/[topage]"); // 頁腳頁碼
        cmd.add("--encoding"); cmd.add("UTF-8");       // 關鍵:防中文亂碼
        cmd.add("--enable-javascript");                // 支持 JS 渲染
        cmd.add("--javascript-delay"); cmd.add("1000"); // 等待 JS 執行 1s
        cmd.add(srcPath);
        cmd.add(destPath);

        try {
            ProcessBuilder pb = new ProcessBuilder(cmd);
            pb.redirectErrorStream(false); // 分離錯誤流
            Process process = pb.start();

            // 異步消費輸出流與錯誤流
            new StreamGobbler(process.getInputStream()).start();
            new StreamGobbler(process.getErrorStream()).start();

            int exitCode = process.waitFor();
            if (exitCode != 0) {
                log.error("wkhtmltopdf 退出碼非 0: {}", exitCode);
                return false;
            }
            return true;
        } catch (IOException | InterruptedException e) {
            log.error("轉換過程異常", e);
            Thread.currentThread().interrupt();
            return false;
        }
    }
}

四、 常用參數速查表(高頻場景)

參數說明示例
--page-size紙張尺寸A4, Letter, Legal
--orientation頁面方向Portrait (默認), Landscape
--margin-top/bottom/left/right四邊邊距10mm, 2cm, 0.5in
--header-html自定義 HTML 頁眉--header-html header.html
--footer-html自定義 HTML 頁腳--footer-html footer.html
--toc生成目錄--toc --toc-header-text "目錄"
--disable-javascript禁用 JS(提速/安全)--disable-javascript
--no-background不打印背景色/圖--no-background
--dpi分辨率300 (高質量打印)

五、 頁眉/頁腳動態變量

在 --header-center、--footer-right 等參數中可使用:

  • [page] 當前頁碼
  • [topage] 總頁數
  • [section] 當前一級標題
  • [subsection] 當前二級標題
  • [date] [time] 系統日期時間
  • [webpage] 源頁面 URL

六、 生產環境避坑指南

  1. 字體缺失導致中文方塊:Linux 需安裝中文字體(如 yum install wqy-zenhei-fonts 或拷貝 .ttf 到 /usr/share/fonts 並 fc-cache -fv)。
  2. 超時設置:針對複雜頁面,配合 --javascript-delay 與 --load-error-handling ignore,並在 Java 層設置 process.waitFor(timeout, TimeUnit.SECONDS)。
  3. 並發控制:wkhtmltopdf 進程佔用較大內存,建議用信號量或線程池限制並發數(如 4 核機器限 2-3 並發)。
  4. 臨時文件清理:轉換過程可能產生臨時文件,定期清理 /tmp 或指定 --user-style-sheet 避免殘留。

七、 替代方案對比

方案優點缺點適用場景
wkhtmltopdfHTML/CSS/JS 原生支持、零模板開發依賴外部二進制、佔用內存大、並發受限現有網頁直接導出、報表樣式複雜
iText / pdfHTML純 Java、高性能、精確控制需編寫模板代碼、CSS 支持不全高併發、精密排版、純數據報表
Flying Saucer (xhtmlrenderer)純 Java、支持 CSS 2.1不支持現代 CSS (Flex/Grid)、JS 無支持簡單 XHTML 轉 PDF

掌握 wkhtmltopdf 的 Java 集成,讓你在「快速交付」與「樣式豐富」之間找到平衡點。下一篇將探討如何構建高可用的 PDF 轉換微服務。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved