PHP导出含图片Word文档的MHT原理与实现

阿麦
阿麦
发布时间:2026-10-14| 4人浏览过
原创

在使用PHP生成Word报表或文档时,开发者常面临如何将HTML样式及其中包含的图片完整导出为 .doc 文件的难题。传统的COM扩展方式效率低且受限于Windows环境,而直接修改后缀名的方法往往导致图片丢失。本文将基于MHTML(Web归档格式)的特性,详细解析如何在PHP中构建一个能自动提取、内嵌图片的Word生成方案。

技术方案对比

在PHP中生成Word文档主要有两种思路,但它们在处理图片和兼容性上表现差异显著:

  1. COM扩展调用 (php_com): 利用服务器上的Office软件引擎(调用 word.application)。这种方式虽然能生成最标准的DOCX/DOC,但需要服务器安装Office,执行时需启动客户端进程,资源占用极高,且在Linux环境下不可用,故不推荐用于高并发场景。
  2. MHTML伪DOC法: Microsoft Word 兼容打开 MHTML (.mht) 文件。MHTML是一种网页存档格式,它允许将HTML页面中的外部资源(如图片、CSS、JS)全部转换为Base64编码并封装在单一文件中。Word能完美渲染此类结构并保持排版。

核心实现逻辑

本方案的核心在于构建一个虚拟的 MHTML 容器。操作流程如下:

第一步:分析HTML并提取资源

获取传入的HTML字符串后,首先需要通过正则表达式过滤掉不需要导出的超链接(可选),并精准提取所有 <img> 标签中的 src 属性。若图片路径为相对路径,需结合给定的绝对根路径拼接成完整的URL,以便后续下载。

第二步:获取图片并编码

对于每一个提取到的图片URL,使用 file_get_contents 获取二进制流。由于MHTML规范要求资源部分使用Base64编码,因此需调用 base64_encode 进行转换。同时,根据文件扩展名(jpg, png等)确定正确的 MIME Type。

第三步:组装MHTML结构

MHTML文件本质上是一个遵循 RFC 1521 的多部分(multipart)MIME消息。我们需要设置:
- Content-Type: multipart/related
- 定义唯一的分隔符(Boundary)

组装顺序通常为:
1. 头部信息
2. 第一个实体:主HTML内容
3. 后续实体:各个内嵌的图片资源
4. 结束标记

第四步:输出下载

生成的字符串数据可以直接写入服务器文件,或通过HTTP响应头发送给浏览器触发下载:

header("Content-Type: application/msword");
header("Content-Disposition: attachment; filename=\"export.doc\";");
echo $mhtmlContent;

完整代码实现

以下是核心的 PHP 实现代码。主要包含两个部分:用于处理逻辑的 getWordDocument 函数和用于构建MHTML格式的 MhtFileMaker 类。

1. 获取Word文档内容的辅助函数

此函数负责解析HTML、清理链接、下载图片并委托给MhtFileMaker类生成最终结果。

复制代码 代码如下:
/**
 * 根据HTML代码获取word文档内容
 * 创建一个本质为mht的文档,该函数会分析文件内容并从远程下载页面中的图片资源
 *
 * @param string $content HTML内容
 * @param string $absolutePath 网页的绝对路径。如果HTML内容里的图片路径为相对路径,那么就需要填写这个参数,来让该函数自动填补成绝对路径。这个参数最后需要以/结束
 * @param bool $isEraseLink 是否去掉HTML内容中的链接
 */
function getWordDocument( $content , $absolutePath = "" , $isEraseLink = true ) {
    $mht = new MhtFileMaker();
    
    // 去掉链接 (可选)
    if ($isEraseLink)
        $content = preg_replace('/<a\s*.*?\s*>(\s*.*?\s*)<\/a>/i' , '$1' , $content);

    $images = array();
    $files = array();
    $matches = array();
    
    // 提取img标签中的src属性 (要求src值必须被引号包围)
    if ( preg_match_all('/<img[.\n]*?src\s*?=\s*[\"\']([^"]+)[\"\'](.*?)\/>/i', $content, $matches ) ) {
        $arrPath = $matches[1];
        for ( $i=0; $i<count($arrPath); $i++ ) {
            $path = $arrPath[$i];
            $imgPath = trim( $path );
            if ( $imgPath != "" ) {
                $files[] = $imgPath;
                
                // 判断是否为绝对路径
                if( substr($imgPath,0,7) == 'http://') {
                    // 已是绝对链接,不加前缀
                } else {
                    // 相对路径,拼接绝对路径
                    $imgPath = $absolutePath.$imgPath;
                }
                $images[] = $imgPath;
            }
        }
    }
    
    // 添加主HTML内容
    $mht->AddContents("tmp.html", $mht->GetMimeType("tmp.html"), $content);
    
    // 遍历并添加图片资源
    for ( $i=0; $i<count($images); $i++ ) {
        $image = $images[$i];
        // 尝试打开图片流
        if ( @fopen($image , 'r') ) {
            $imgcontent = @file_get_contents( $image );
            if ( $imgcontent ) {
                $mht->AddContents($files[$i], $mht->GetMimeType($image), $imgcontent);
            }
        } else {
            // 记录错误或忽略
            echo "file:".$image." not exist!<br />";
        }
    }
    
    return $mht->GetFile();
}

2. MHT 文件生成类 (MhtFileMaker)

该类负责严格按照RFC协议规范,构建多部分MIME消息。

复制代码 代码如下:
?php
class MhtFileMaker{
    var $config = array();
    var $headers = array();
    var $headers_exists = array();
    var $files = array();
    var $boundary;
    var $dir_base;
    var $page_first;

    function MhtFile($config = array()){
        // 初始化配置
    }

    function SetHeader($header){
        $this->headers[] = $header;
        $key = strtolower(substr($header, 0, strpos($header, ':')));
        $this->headers_exists[$key] = TRUE;
    }

    function SetFrom($from){
        $this->SetHeader("From: $from");
    }

    function SetSubject($subject){
        $this->SetHeader("Subject: $subject");
    }

    function SetDate($date = NULL, $istimestamp = FALSE){
        if ($date == NULL) {
            $date = time();
        }
        if ($istimestamp == TRUE) {
            $date = date('D, d M Y H:i:s O', $date);
        }
        $this->SetHeader("Date: $date");
    }

    function SetBoundary($boundary = NULL){
        if ($boundary == NULL) {
            $this->boundary = '--' . strtoupper(md5(mt_rand())) . '_MULTIPART_MIXED';
        } else {
            $this->boundary = $boundary;
        }
    }

    function SetBaseDir($dir){
        $this->dir_base = str_replace("\\", "/", realpath($dir));
    }

    function SetFirstPage($filename){
        $this->page_first = str_replace("\\", "/", realpath("{$this->dir_base}/$filename"));
    }

    // ... (此处省略内部私有辅助方法如 AutoAddFiles, AddDir 等,它们主要用于文件系统操作) ...

    /**
     * 添加文件内容到集合中
     */
    function AddContents($filepath, $mimetype, $filecont, $encoding = NULL){
        if ($encoding == NULL) {
            // 默认使用 Base64 编码
            $filecont = chunk_split(base64_encode($filecont), 76);
            $encoding = 'base64';
        }
        $this->files[] = array(
            'filepath' => $filepath,
            'mimetype' => $mimetype,
            'filecont' => $filecont,
            'encoding' => $encoding
        );
    }

    /**
     * 检查必要的 Headers 和 Boundary
     */
    function CheckHeaders(){
        if (!array_key_exists('date', $this->headers_exists)) {
            $this->SetDate(NULL, TRUE);
        }
        if ($this->boundary == NULL) {
            $this->SetBoundary();
        }
    }

    function GetMimeType($filename){
        $pathinfo = pathinfo($filename);
        switch ($pathinfo['extension']) {
            case 'htm': case 'html': return 'text/html'; break;
            case 'txt': case 'cgi': case 'php': return 'text/plain'; break;
            case 'css': return 'text/css'; break;
            case 'jpg': case 'jpeg': case 'jpe': return 'image/jpeg'; break;
            case 'gif': return 'image/gif'; break;
            case 'png': return 'image/png'; break;
            default: return 'application/octet-stream'; break;
        }
    }

    /**
     * 生成最终的 MHT 字符串
     */
    function GetFile(){
        $this->CheckHeaders();
        
        $contents = implode("\r\n", $this->headers);
        $contents .= "\r\n";
        $contents .= "MIME-Version: 1.0\r\n";
        $contents .= "Content-Type: multipart/related;\r\n";
        $contents .= "\tboundary=\"{$this->boundary}\";\r\n";
        $contents .= "\ttype=\"" . $this->files[0]['mimetype'] . "\"\r\n";
        $contents .= "X-MimeOLE: Produced By Mht File Maker v1.0 beta\r\n";
        $contents .= "\r\n";
        $contents .= "This is a multi-part message in MIME format.\r\n";
        $contents .= "\r\n";
        
        foreach ($this->files as $file) {
            $contents .= "--{$this->boundary}\r\n";
            $contents .= "Content-Type: $file[mimetype]\r\n";
            $contents .= "Content-Transfer-Encoding: $file[encoding]\r\n";
            $contents .= "Content-Location: $file[filepath]\r\n";
            $contents .= "\r\n";
            $contents .= $file['filecont'];
            $contents .= "\r\n";
        }
        
        $contents .= "--{$this->boundary}--\r\n";
        return $contents;
    }
}
?>

使用方法与注意事项

在实际使用中,只需确保 MhtFileMaker 类已加载,然后调用核心函数即可:

复制代码 代码如下:
// $content 是你的 HTML 源代码
// 第二个参数是图片所在的基础URL,确保相对路径能转为绝对路径
$fileContent = getWordDocument($content, "https://example.com/images/");

// 保存为文件
$fp = fopen("test.doc", 'w');
fwrite($fp, $fileContent);
fclose($fp);

排错提示:

  • 图片不显示:最常见原因是 HTML 中的图片路径无法被访问(防火墙拦截、非公网地址)或路径未正确拼接。请检查 fopen 是否能成功获取图片流。
  • HTML 标签闭合问题:正则表达式提取 img 标签时,假设 src 的值是被引号包裹的。如果源码中存在不规范写法(如 <img src=img.png>),可能需要先清洗 HTML。
  • 大文件内存溢出:如果 HTML 中包含大量高清大图,将所有图片 Base64 编码后会导致内存激增。建议在测试环境验证图片数量,必要时增加 memory_limit 或对图片进行压缩预处理。

总结

利用 MHTML 特性配合 PHP 原生函数,是一种轻量级、无依赖的 Word 文档生成方案。它巧妙地规避了 Office COM 组件的性能瓶颈和跨平台限制,实现了图文混排的完美导出。对于简单的报表生成、报告导出需求,这套代码提供了稳定可靠的底层支持。 以上就是PHP导出含图片Word文档的MHT原理与实现的详细内容,更多关于PHP Word导出技巧的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。