使用PHP Mailparse如何区分邮件附件图片与内嵌图片?
解决方案
核心判断依据
邮件协议中两类图片的MIME属性存在明确差异,你可以通过以下三个维度组合判断,准确率可以覆盖绝大多数合规邮件场景:
- 优先读取每个邮件部分的
Content-Disposition头:值为attachment的是用户主动上传的附件,值为inline的大概率是正文内嵌图片 - 补充校验
Content-ID(简称CID)字段:所有被正文HTML引用的内嵌图片都会带有CID,且会在HTML的img标签的src属性中以cid:xxx的形式被引用,你可以把所有正文中出现的CID全部提取出来,只要某张图片的CID在这个列表里,就判定为内嵌图片 - 兜底校验规则:极少数不规范邮件可能缺失
Content-Disposition头,此时如果图片的文件名是类似logo.png、signature.jpg这类通用签名/装饰图命名,且没有符合用户上传附件的常规命名特征,也可以辅助判定为内嵌资源,规则可以根据你的业务场景灵活调整
基于PHP Mailparse的实现步骤
- 先用
mailparse_msg_parse_file或者mailparse_msg_parse解析原始邮件内容,拿到邮件的MIME结构 - 遍历所有邮件部分,先提取所有
text/html类型的正文内容,用正则匹配出所有src="cid:xxx"里的CID值,存到$inline_cids数组中 - 再次遍历所有图片类型的邮件部分,按规则筛选:
- 若该部分的
Content-Disposition头值为attachment,直接保留为用户上传附件 - 若
Content-Disposition为inline或者没有该头,读取它的Content-ID,去掉头尾的尖括号后判断是否在$inline_cids数组里,是的话判定为内嵌图片直接丢弃,不在的话保留为附件
- 若该部分的
- 注意处理多部分嵌套的情况,比如
mixed、alternative、related类型的MIME部分,要递归遍历所有子部分,不要漏掉嵌套的附件
核心代码片段示例
// 初始化解析器,加载原始邮件内容 $mime = mailparse_msg_create(); mailparse_msg_parse($mime, $raw_email_content); $structure = mailparse_msg_get_structure($mime); // 第一步:提取正文中所有引用的内嵌图片CID $inline_cids = []; foreach ($structure as $part_id) { $part = mailparse_msg_get_part($mime, $part_id); $part_data = mailparse_msg_get_part_data($part); if ($part_data['content-type'] == 'text/html') { $body = mailparse_msg_extract_part($mime, $part_id, $raw_email_content); // 正则匹配所有cid引用 preg_match_all('/src=["\']cid:([^"\']+)["\']/i', $body, $matches); if (!empty($matches[1])) { $inline_cids = array_merge($inline_cids, array_map('trim', $matches[1])); } } } // 第二步:筛选保留用户主动上传的附件图片 $attachments = []; foreach ($structure as $part_id) { $part = mailparse_msg_get_part($mime, $part_id); $part_data = mailparse_msg_get_part_data($part); // 仅处理图片类型的部分 if (str_starts_with($part_data['content-type'], 'image/')) { $disposition = $part_data['content-disposition'] ?? ''; $cid = trim($part_data['content-id'] ?? '', '<> '); // 满足以下条件判定为用户上传附件,予以保留 if ($disposition == 'attachment' || !in_array($cid, $inline_cids)) { $attachments[] = [ 'name' => $part_data['disposition-filename'] ?? $part_data['content-name'] ?? 'untitled', 'type' => $part_data['content-type'], 'content' => mailparse_msg_extract_part($mime, $part_id, $raw_email_content) ]; } } } // 释放解析资源 mailparse_msg_free($mime);
注:代码中的str_starts_with是PHP8+内置函数,如果使用更低PHP版本,可以替换为substr($part_data['content-type'], 0, 6) === 'image/'实现同等效果
异常场景兼容
- 纯文本邮件不存在内嵌图片,所有图片附件都可以直接全部保留
- 少数老旧邮件客户端生成的邮件可能存在头信息填写不规范的情况,建议同时开启
Content-Disposition和CID匹配双重校验,误判率可控制在1%以内
内容的提问来源于stack exchange,提问作者Joe Bloggs
相关产品推荐
相关产品推荐

