You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP使用phpMergeDocx合并docx时所有图片被首份文件图片替换问题求助

问题原因

你现有的代码只合并了docx的正文XML内容,没有处理docx内置的媒体资源和关联映射,所以出现图片被覆盖的问题:

  • docx是ZIP格式的压缩包,所有嵌入图片默认存放在word/media/目录下,不同docx的图片命名都是image1.png、image2.jpg这类递增序列,直接合并会出现重名覆盖
  • 正文XML中的图片通过rId和word/_rels/document.xml.rels文件里的资源路径做关联,两个docx的rId序列也会重复,导致引用指向错误的资源
修复实现

需要额外处理媒体文件、关系映射两个部分,修改后的代码如下(兼容PHP7及以上版本):

function merge2Docx($document1, $document2, $outputfile){
    if (!file_exists($document1) || !file_exists($document2)) {
        return null;
    }
    $zip1 = new clsTbsZip();
    $zip2 = new clsTbsZip();
    
    // 读取第一个文档的所有内容
    $zip1->Open($document1);
    $content1 = $zip1->FileRead('word/document.xml');
    // 读取第一个文档的关系映射
    $rels1 = $zip1->FileRead('word/_rels/document.xml.rels');
    // 读取第一个文档的所有媒体文件
    $media1 = [];
    foreach ($zip1->GetFileList() as $file) {
        if (substr($file, 0, 11) === 'word/media/') {
            $media1[$file] = $zip1->FileRead($file);
        }
    }
    $zip1->Close();

    // 提取第一个文档的正文内容
    $p = strpos($content1, '<w:body');
    if ($p === false) return null;
    $p = strpos($content1, '>', $p);
    $content1 = substr($content1, $p + 1);
    $p = strpos($content1, '</w:body>');
    if ($p === false) return null;
    $content1_body = substr($content1, 0, $p);

    // 处理第二个文档
    $zip2->Open($document2);
    $content2 = $zip2->FileRead('word/document.xml');
    $rels2 = $zip2->FileRead('word/_rels/document.xml.rels');
    // 获取第二个文档现有媒体文件的最大序号,避免重名
    $max_media_idx = 0;
    foreach ($zip2->GetFileList() as $file) {
        if (substr($file, 0, 16) === 'word/media/image') {
            preg_match('/image(\d+)\./', $file, $matches);
            if (isset($matches[1]) && intval($matches[1]) > $max_media_idx) {
                $max_media_idx = intval($matches[1]);
            }
        }
    }
    // 获取第二个文档现有rId的最大序号,避免冲突
    $max_rid = 0;
    preg_match_all('/Id="rId(\d+)"/', $rels2, $rid_matches);
    if (!empty($rid_matches[1])) {
        $max_rid = max(array_map('intval', $rid_matches[1]));
    }

    // 处理第一个文档的关系映射和媒体文件
    $rid_map = [];
    preg_match_all('/<Relationship Id="rId(\d+)" Type=".*image" Target="media\/([^"]+)"\/?>/', $rels1, $rel_matches, PREG_SET_ORDER);
    foreach ($rel_matches as $rel) {
        $old_rid = $rel[1];
        $old_media_name = $rel[2];
        $new_rid = ++$max_rid;
        $new_media_idx = ++$max_media_idx;
        $new_media_name = preg_replace('/image\d+\./', "image{$new_media_idx}.", $old_media_name);
        // 记录rid映射
        $rid_map["rId{$old_rid}"] = "rId{$new_rid}";
        // 追加新的关系到第二个文档的rels
        $new_rel = str_replace(["Id=\"rId{$old_rid}\"", "Target=\"media/{$old_media_name}\""], ["Id=\"rId{$new_rid}\"", "Target=\"media/{$new_media_name}\""], $rel[0]);
        $rels2 = str_replace('</Relationships>', $new_rel . '</Relationships>', $rels2);
        // 重命名媒体文件并写入第二个文档
        $old_media_path = "word/media/{$old_media_name}";
        if (isset($media1[$old_media_path])) {
            $zip2->FileAdd("word/media/{$new_media_name}", $media1[$old_media_path], TBSZIP_STRING);
        }
    }

    // 替换第一个文档正文里的所有旧rId为新的
    foreach ($rid_map as $old_rid => $new_rid) {
        $content1_body = str_replace($old_rid, $new_rid, $content1_body);
    }

    // 拼接正文内容,加分页符
    $content1_body = '<w:p><w:r><w:br w:type="page" /><w:lastRenderedPageBreak/></w:r></w:p>' . $content1_body;
    // 合并到第二个文档正文
    $p = strpos($content2, '</w:body>');
    if ($p === false) return null;
    $content2 = substr($content2, 0, $p) . $content1_body . substr($content2, $p);
    $zip2->FileReplace('word/document.xml', $content2, TBSZIP_STRING);
    // 更新第二个文档的rels文件
    $zip2->FileReplace('word/_rels/document.xml.rels', $rels2, TBSZIP_STRING);

    // 输出文件
    $zip2->Flush(TBSZIP_FILE, $outputfile);
    $zip2->Close();

    return file_exists($outputfile) ? $outputfile : null;
}
注意事项
  • 上述代码仅处理了图片类型的资源,如果你的docx里还有嵌入的OLE对象、图表等其他类型资源,需要对应扩展关系类型的匹配规则
  • 如果需要合并两个以上的docx,循环执行上述逻辑即可
  • 操作前建议备份原文件,避免数据丢失

内容的提问来源于stack exchange,提问作者An Pham Karion Co.Ltd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.04 18:36:02