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

PHP 匯出 Word 文件完整方案:MHT 格式嵌入圖片、樣式完美保真

     編輯:PHP 綜合知識

核心原理:為什麼選 MHTML (.mht)?

Word 能直接開啟 HTML,但外部資源(圖片、CSS)會丟失。MHTML (MIME HTML) 將所有資源以 Base64 編碼內嵌於單一檔案中,Word 可完美識別並渲染。只需將 .mht 改為 .doc 副檔名即可。

完整實作:MhtFileMaker 類別

<?php
/**
 * MHTML 檔案產生器
 * 支援:HTML 內容、圖片內嵌、CSS 內嵌、自訂 MIME 類型
 */
class MhtFileMaker {
    private array $headers = [];
    private array $parts = [];
    private string $boundary;

    public function __construct() {
        $this->boundary = '--' . strtoupper(md5(microtime() . mt_rand())) . '_MHT_PART';
        $this->headers['MIME-Version'] = '1.0';
        $this->headers['Content-Type'] = "multipart/related; boundary=\"{$this->boundary}\"; type=\"text/html\"";
        $this->headers['X-MimeOLE'] = 'Produced By PHP MhtFileMaker';
    }

    public function setHeader(string $name, string $value): void {
        $this->headers[$name] = $value;
    }

    public function addPart(string $location, string $mimeType, string $content, string $encoding = 'base64'): void {
        if ($encoding === 'base64') {
            $content = chunk_split(base64_encode($content), 76);
        }
        $this->parts[] = compact('location', 'mimeType', 'content', 'encoding');
    }

    public function getMimeType(string $filename): string {
        $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
        return match ($ext) {
            'htm', 'html' => 'text/html',
            'txt' => 'text/plain',
            'css' => 'text/css',
            'jpg', 'jpeg', 'jpe' => 'image/jpeg',
            'png' => 'image/png',
            'gif' => 'image/gif',
            'webp' => 'image/webp',
            'svg' => 'image/svg+xml',
            'woff' => 'font/woff',
            'woff2' => 'font/woff2',
            default => 'application/octet-stream',
        };
    }

    public function build(): string {
        $out = [];
        foreach ($this->headers as $k => $v) {
            $out[] = "{$k}: {$v}";
        }
        $out[] = '';
        $out[] = 'This is a multi-part message in MIME format.';
        $out[] = '';

        foreach ($this->parts as $part) {
            $out[] = "--{$this->boundary}";
            $out[] = "Content-Type: {$part['mimeType']}";
            $out[] = "Content-Transfer-Encoding: {$part['encoding']}";
            $out[] = "Content-Location: {$part['location']}";
            $out[] = '';
            $out[] = $part['content'];
            $out[] = '';
        }
        $out[] = "--{$this->boundary}--";
        return implode("\r\n", $out);
    }

    public function save(string $filename): bool {
        $content = $this->build();
        return file_put_contents($filename, $content) !== false;
    }
}

核心功能:HTML 轉 Word 主函數

<?php
/**
 * 將 HTML 內容轉換為可含圖片的 Word 文件內容
 * @param string $html HTML 來源內容
 * @param string $baseUrl 圖片相對路徑補全基礎 URL(需以 / 結尾)
 * @param bool $stripLinks 是否移除超連結
 * @return string MHTML 內容
 */
function htmlToWord(string $html, string $baseUrl = '', bool $stripLinks = true): string {
    $mht = new MhtFileMaker();

    // 1. 移除超連結(保留文字)
    if ($stripLinks) {
        $html = preg_replace('/<a\s[^>]*>(.*?)<\/a>/is', '$1', $html);
    }

    // 2. 提取所有圖片 src
    $imageUrls = [];
    $imageLocations = [];
    if (preg_match_all('/<img[^>]+src\s*=\s*["\']([^"\']+)["\']/i', $html, $matches)) {
        foreach ($matches[1] as $idx => $src) {
            $src = trim($src);
            if ($src === '' || str_starts_with($src, 'data:')) continue;

            // 處理相對路徑
            $fullUrl = $src;
            if (!preg_match('/^https?:\/\//i', $src)) {
                $fullUrl = rtrim($baseUrl, '/') . '/' . ltrim($src, '/');
            }

            // 產生唯一內部位置識別碼
            $location = "image_{$idx}_" . basename($src);
            $imageUrls[$src] = $fullUrl;
            $imageLocations[$src] = $location;

            // 替換 HTML 中的 src 為內部位置
            $html = str_replace($src, $location, $html);
        }
    }

    // 3. 加入主 HTML 部分
    $mht->addPart('main.html', 'text/html', $html);

    // 4. 下載並加入圖片
    foreach ($imageUrls as $originalSrc => $fullUrl) {
        $location = $imageLocations[$originalSrc];
        $mimeType = $mht->getMimeType($location);

        $ctx = stream_context_create([
            'http' => ['timeout' => 10, 'user_agent' => 'Mozilla/5.0 PHP Word Export'],
            'ssl' => ['verify_peer' => false, 'verify_peer_name' => false], // 開發環境可關閉驗證
        ]);

        $imgData = @file_get_contents($fullUrl, false, $ctx);
        if ($imgData !== false) {
            $mht->addPart($location, $mimeType, $imgData);
        } else {
            error_log("Word 匯出:無法下載圖片 {$fullUrl}");
        }
    }

    return $mht->build();
}

使用範例

<?php
require_once 'MhtFileMaker.php';

$html = '<h1>產品報價單</h1>
<p>日期:' . date('Y-m-d') . '</p>
<img src="/images/logo.png" alt="Logo">
<table border="1"><tr><th>品項</th><th>價格</th></tr>
<tr><td>商品 A</td><td>$100</td></tr></table>';

$wordContent = htmlToWord($html, 'https://example.com/');

// 直接下載
header('Content-Type: application/msword');
header('Content-Disposition: attachment; filename="quote_' . date('Ymd') . '.doc"');
header('Content-Length: ' . strlen($wordContent));
echo $wordContent;
exit;

// 或儲存檔案
// file_put_contents('storage/quote.doc', $wordContent);

進階擴充:支援 CSS 樣式內嵌

<?php
// 在 htmlToWord() 中加入 CSS 處理
if (preg_match_all('/<link[^>]+rel=["\']stylesheet["\'][^>]+href=["\']([^"\']+)["\']/i', $html, $cssMatches)) {
    foreach ($cssMatches[1] as $idx => $href) {
        $fullCssUrl = $href;
        if (!preg_match('/^https?:\/\//i', $href)) {
            $fullCssUrl = rtrim($baseUrl, '/') . '/' . ltrim($href, '/');
        }
        $cssContent = @file_get_contents($fullCssUrl);
        if ($cssContent) {
            $location = "style_{$idx}.css";
            $mht->addPart($location, 'text/css', $cssContent);
            // 將 link 標籤替換為內嵌 style
            $html = str_replace($cssMatches[0][$idx], "<style>{$cssContent}</style>", $html);
        }
    }
}

注意事項與限制

項目說明
檔案大小Base64 編碼約增大 33%,大量圖片建議改用 PHPWord 產生真正的 .docx (OOXML)
複雜排版MHTML 排版能力受限於 Word 的 HTML 渲染引擎,複雜表格、浮動、Flex/Grid 可能失效
中文檔名下載時需 RFC 5987 編碼:filename*=UTF-8''%E6%8A%A5%E8%A1%A8.doc
相容性Word 2007+ 完美支援,LibreOffice/OpenOffice 亦可開啟
安全性嚴禁直接使用者輸入的 HTML 未經淨化,防 XSS 與 SSRF(file_get_contents 讀取內網)

替代方案:PHPWord (PhpOffice)

<?php
// composer require phpoffice/phpword
use PhpOffice\PhpWord\IOFactory;
use PhpOffice\PhpWord\TemplateProcessor;

// 範本式產生(推薦正式專案)
$template = new TemplateProcessor('template.docx');
$template->setValue('title', '報價單');
$template->setValue('date', date('Y-m-d'));
$template->save('output.docx');

PHPWord 產生真正的 OOXML 格式,支援完整 Word 功能(目錄、頁碼、樣式、圖表),是企業級專案的首選。

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