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

PHP展示EML文件时内嵌图片无法加载问题求助

问题描述

使用PHP代码展示EML文件内容时,邮件正文显示正常,但内嵌图片无法加载,且正文结束后会显示图片相关的原始文本内容,示例如下:

--0000000000003cea95058bbee080-- --0000000000003cea99058bbee082 Content-Type: image/jpeg; name="=?UTF-8?B?16rXnNeV16DXlCDXqNen15XXkdenINeh16jXp9eZ15Qu? =?UTF-8?B?anBn?=" Content-Disposition: attachment; filename="=?UTF-8?B?16rXnNeV16DXlCDXqNen15XXkdenINeh16jXp9eZ15QuanBn?=" Content-Transfer-Encoding: base64 X-Attachment-Id: f_jx4j1uzv0 Content-ID: /9j/4AAQSkZJRgABAQEASwBLAAD/4RDqRXhpZgAATU0AKgAAAAgABgALAAIAAAAmAAAIYgESAAMA AAABAAMAAAExAAIAAAAmAAAIiAEyAAIAAAAUAAAIrodpAAQAAAABAAAIwuocAAcAAAgMAAAAVgAA AAAc6gAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

由于使用共享服务器,无法安装PHP类库或扩展包,需要排查代码问题并修复。

原使用的PHP代码:

<?php

error_reporting(-1);

$EML_FILE_PATH = '';
$PICTURE_DIRECTORY_PATH = 'img/';

$filename = 'sample.eml';
$eml_file = $EML_FILE_PATH . $filename;

// Improved error handling when opening and reading the file.
if (!$handle = fopen($eml_file, 'r')) {
    die('Cannot open file (' . $eml_file . ')');
}
$content = fread($handle, filesize($eml_file));
fclose($handle);

if ($content === false) {
    die('Error reading file (' . $eml_file . ')');
}

// Define separators for different sections of the email.
if (strpos($content, '------_') !== false) {
    $separator = '------_';
} else {
    $separator = '------=_';
}
$aContent = explode($separator, $content);

$aImages = array();
$thisHTMLContent = '';

foreach ($aContent as $thisContent) {
    if (strpos($thisContent, 'Content-Type: text/html') !== false) {
        // Correct handling of the email HTML body.
        $thisContent = substr($thisContent, strpos($thisContent, '<!DOCTYPE'));
        $thisHTMLContent = quoted_printable_decode($thisContent);
    }

    // Function to process images (DRY principle).
    function processImage($thisContent, $imgType) {
        $begin = strpos($thisContent, 'Content-ID: <') + 13;
        $long = strpos(substr($thisContent, $begin), '>');
        $img_id = substr($thisContent, $begin, $long);
        $img_name = substr($thisContent, strpos($thisContent, 'name="') + 6, strpos($thisContent, ".$imgType\"") - strpos($thisContent, 'name="') - 6);
        $img_location = $img_name; // Simplified logic assuming name is used as location.
        $searched = 'Content-ID: <' . $img_id . '>';
        $Content_ID_pos = strpos($thisContent, $searched);
        $img_base64 = substr($thisContent, $Content_ID_pos + strlen($searched) + 1);
        return array('id' => $img_id, 'name' => $img_name, 'location' => $img_location, 'type' => $imgType, 'base64' => $img_base64);
    }

    if (strpos($thisContent, 'Content-Type: image/gif;') !== false) {
        $aImages[] = processImage($thisContent, 'gif');
    }
    if (strpos($thisContent, 'Content-Type: image/jpeg;') !== false) {
        $aImages[] = processImage($thisContent, 'jpg');
    }
}

foreach ($aImages as $image) {
    $imageData = 'data:image/' . $image['type'] . ';base64,' . trim($image['base64']);
    $thisHTMLContent = str_replace('cid:' . $image['id'], $imageData, $thisHTMLContent);
}

echo $thisHTMLContent;
?>
问题排查与修复

1. 修复分隔符处理逻辑

邮件MIME分隔符以--开头,需以此拆分内容,并过滤空内容和结束标记:

// 替换原分隔符逻辑
$separator = '--';
$aContent = explode($separator, $content);
// 过滤空内容和结束标记
$aContent = array_filter($aContent, function($item) {
    $trimmed = trim($item);
    return !empty($trimmed) && $trimmed !== '--';
});

2. 兼容不同格式的Content-ID

处理带<>和不带<>的Content-ID格式:

// 修改processImage函数中的Content-ID提取逻辑
$cidPos = strpos($thisContent, 'Content-ID:');
if ($cidPos === false) return false;
$cidLine = substr($thisContent, $cidPos);
$cidLine = substr($cidLine, 0, strpos($cidLine, "\n"));
$img_id = trim(str_replace(['Content-ID:', '<', '>'], '', $cidLine));

3. 解码文件名的UTF-8编码

解析=?UTF-8?B?...?=格式的文件名,使用mb_decode_mimeheader解码:

// 修改文件名提取逻辑
$namePos = strpos($thisContent, 'name="');
if ($namePos !== false) {
    $nameEnd = strpos($thisContent, '"', $namePos + 6);
    $encodedName = substr($thisContent, $namePos + 6, $nameEnd - $namePos -6);
    $img_name = mb_decode_mimeheader($encodedName);
} else {
    $img_name = 'unknown.' . $imgType;
}

4. 正确提取Base64内容

找到Content-Transfer-Encoding: base64之后的空行,提取后续的Base64内容,并过滤空白行:

// 修改Base64内容提取逻辑
$base64Pos = strpos($thisContent, 'Content-Transfer-Encoding: base64');
if ($base64Pos === false) return false;
// 找到base64行后的第一个空行
$emptyLinePos = strpos($thisContent, "\n\n", $base64Pos);
if ($emptyLinePos === false) $emptyLinePos = strpos($thisContent, "\r\n\r\n", $base64Pos);
$img_base64 = substr($thisContent, $emptyLinePos + 2);
// 过滤空白行和末尾的分隔符残留
$img_base64 = preg_replace('/\s+/', '', $img_base64);
$img_base64 = trim($img_base64, "\r\n");

5. 调整函数定义位置

将processImage函数移到循环外部,避免重复定义警告。

6. 正确提取HTML正文

找到Content-Type: text/html后的空行,提取后续的HTML内容:

// 修改HTML内容提取逻辑
if (strpos($thisContent, 'Content-Type: text/html') !== false) {
    $htmlStart = strpos($thisContent, "\n\n");
    if ($htmlStart === false) $htmlStart = strpos($thisContent, "\r\n\r\n");
    if ($htmlStart !== false) {
        $htmlContent = substr($thisContent, $htmlStart + 2);
        $thisHTMLContent = quoted_printable_decode($htmlContent);
    }
}
修正后的完整代码
<?php
error_reporting(-1);

$EML_FILE_PATH = '';
$filename = 'sample.eml';
$eml_file = $EML_FILE_PATH . $filename;

// 读取EML文件
if (!$handle = fopen($eml_file, 'r')) {
    die('Cannot open file (' . $eml_file . ')');
}
$content = fread($handle, filesize($eml_file));
fclose($handle);

if ($content === false) {
    die('Error reading file (' . $eml_file . ')');
}

// 拆分MIME部分
$separator = '--';
$aContent = explode($separator, $content);
$aContent = array_filter($aContent, function($item) {
    $trimmed = trim($item);
    return !empty($trimmed) && $trimmed !== '--';
});

$aImages = array();
$thisHTMLContent = '';

// 处理图片的函数,移到循环外
function processImage($thisContent, $imgType) {
    // 提取Content-ID
    $cidPos = strpos($thisContent, 'Content-ID:');
    if ($cidPos === false) return false;
    $cidLine = substr($thisContent, $cidPos);
    $cidLine = substr($cidLine, 0, strpos($cidLine, "\n"));
    $img_id = trim(str_replace(['Content-ID:', '<', '>'], '', $cidLine));
    
    // 提取并解码文件名
    $img_name = 'unknown.' . $imgType;
    $namePos = strpos($thisContent, 'name="');
    if ($namePos !== false) {
        $nameEnd = strpos($thisContent, '"', $namePos + 6);
        $encodedName = substr($thisContent, $namePos + 6, $nameEnd - $namePos -6);
        $img_name = mb_decode_mimeheader($encodedName);
    }
    
    // 提取Base64内容
    $base64Pos = strpos($thisContent, 'Content-Transfer-Encoding: base64');
    if ($base64Pos === false) return false;
    $emptyLinePos = strpos($thisContent, "\n\n", $base64Pos);
    if ($emptyLinePos === false) $emptyLinePos = strpos($thisContent, "\r\n\r\n", $base64Pos);
    if ($emptyLinePos === false) return false;
    
    $img_base64 = substr($thisContent, $emptyLinePos + 2);
    $img_base64 = preg_replace('/\s+/', '', $img_base64);
    $img_base64 = trim($img_base64, "\r\n");
    
    return [
        'id' => $img_id,
        'name' => $img_name,
        'type' => $imgType,
        'base64' => $img_base64
    ];
}

// 遍历处理每个MIME部分
foreach ($aContent as $thisContent) {
    // 处理HTML正文
    if (strpos($thisContent, 'Content-Type: text/html') !== false) {
        $htmlStart = strpos($thisContent, "\n\n");
        if ($htmlStart === false) $htmlStart = strpos($thisContent, "\r\n\r\n");
        if ($htmlStart !== false) {
            $htmlContent = substr($thisContent, $htmlStart + 2);
            $thisHTMLContent = quoted_printable_decode($htmlContent);
        }
    }
    
    // 处理GIF图片
    if (strpos($thisContent, 'Content-Type: image/gif') !== false) {
        $imgData = processImage($thisContent, 'gif');
        if ($imgData) $aImages[] = $imgData;
    }
    // 处理JPEG图片
    if (strpos($thisContent, 'Content-Type: image/jpeg') !== false) {
        $imgData = processImage($thisContent, 'jpg');
        if ($imgData) $aImages[] = $imgData;
    }
}

// 替换HTML中的cid引用为base64数据
foreach ($aImages as $image) {
    if (!empty($image['id']) && !empty($image['base64'])) {
        $imageData = 'data:image/' . $image['type'] . ';base64,' . $image['base64'];
        $thisHTMLContent = str_replace('cid:' . $image['id'], $imageData, $thisHTMLContent);
    }
}

echo $thisHTMLContent;
?>

内容的提问来源于stack exchange,提问作者Suneth Kalhara

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 22:39:50