如何用TIdMessage处理含内联图片的MIME格式并还原HTML?
提取MIME内联图片Base64并还原HTML渲染的解决方案
问题描述
使用TIdMessage库加载本地MIME文本文件时,已能识别内联附件的Content-Disposition和Content-Type,但无法提取内联图片的原始Base64内容。尝试用OpenLoadStream访问TIdAttachment时,得到的是二进制图片数据而非Base64字符串,需要将Base64转换为data URI插入HTML以还原图片渲染。
提供的MIME内容
Content-Type: multipart/alternative; boundary="qawqKLHOZYgscUPhZKw5hacv=_UlERjiEj" MIME-Version: 1.0 Date: Mon, 25 Mar 2024 10:17:34 +0800 This is a multi-part message in MIME format --qawqKLHOZYgscUPhZKw5hacv=_UlERjiEj Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Content-Disposition: inline Sample HTML --qawqKLHOZYgscUPhZKw5hacv=_UlERjiEj Content-Type: multipart/related; type="text/html"; boundary="5SZQJxkhHuQyExC8cj=_sPNysz4gQuoIGj" --5SZQJxkhHuQyExC8cj=_sPNysz4gQuoIGj Content-Type: text/html; charset="utf-8" Content-Transfer-Encoding: quoted-printable Content-Disposition: inline <P><STRONG>Sample HTML</STRONG></P> <P><STRONG><IMG style=3D"HEIGHT: 184px; WIDTH: 194px" border=3D0 hspac= e=3D0 alt=3D"" src=3D"cid:/Desktop/potato.gif" width=3D495 align=3Dbas= eline height=3D497></STRONG></P> --5SZQJxkhHuQyExC8cj=_sPNysz4gQuoIGj Content-Type: image/gif; name="/Desktop/potato.gif" Content-Transfer-Encoding: base64 Content-Disposition: inline; filename="/Desktop/potato.gif" Content-ID: </Desktop/potato.gif> <base 64 data> --5SZQJxkhHuQyExC8cj=_sPNysz4gQuoIGj-- --qawqKLHOZYgscUPhZKw5hacv=_UlERjiEj-- .
初始代码
var vMsg := TIdMessage.Create(nil); try vMsg.LoadFromFile('D:\Desktop\Complete MIME 2.txt'); for var i := 0 to vMsg.MessageParts.Count - 1 do begin var o := vMsg.MessageParts.Items[i]; if (o.PartType = TIdMessagePartType.mptAttachment) then begin if o.ContentDisposition.Equals('inline') then begin var sContentID := o.ContentID.Substring(1, o.ContentID.Length - 2); // how to retrieve inline image base 64? end; end; end; finally vMsg.Free; end;
更新尝试代码
var vAttachment := TIdAttachment(o); var vAttachmentStream := vAttachment.OpenLoadStream; var vStringStream := TStringStream.Create; try vStringStream.LoadFromStream(vAttachmentStream); vStringStream.Position := 0; OutputDebugString(PChar(vStringStream.DataString)); finally vAttachment.CloseLoadStream; vStringStream.Free; end;
解决方案
核心原理
TIdMessage默认会自动解码Content-Transfer-Encoding指定的编码(如Base64),所以直接通过OpenLoadStream获取的是解码后的二进制图片数据。要获取原始Base64字符串,需禁止自动解码,保留MIME部分的原始内容。
完整实现代码
var vMsg := TIdMessage.Create(nil); try // 关键:禁止自动解码,保留原始Base64内容 vMsg.NoDecode := True; vMsg.LoadFromFile('D:\Desktop\Complete MIME 2.txt'); var htmlContent := ''; // 第一步:提取并解码HTML内容(HTML使用quoted-printable编码) for var i := 0 to vMsg.MessageParts.Count - 1 do begin var o := vMsg.MessageParts.Items[i]; if o is TIdText then begin var txt := TIdText(o); if txt.ContentType.StartsWith('text/html') then begin // 解码quoted-printable编码的HTML htmlContent := TIdDecoderQuotedPrintable.DecodeString(txt.Body.Text, IndyTextEncoding_UTF8); Break; end; end; end; // 第二步:提取内联图片的Base64并替换HTML中的CID引用 for var i := 0 to vMsg.MessageParts.Count - 1 do begin var o := vMsg.MessageParts.Items[i]; if (o.PartType = TIdMessagePartType.mptAttachment) then begin var att := TIdAttachment(o); if att.ContentDisposition.Equals('inline') then begin // 清理ContentID的<>包裹符 var sContentID := att.ContentID.Substring(1, att.ContentID.Length - 2); // 获取原始Base64内容(因NoDecode=True,Body即为Base64字符串) var base64Data := att.Body.Text; // 移除Base64中的换行符(MIME标准会插入换行,需清理) base64Data := StringReplace(base64Data, #13#10, '', [rfReplaceAll]); base64Data := StringReplace(base64Data, #10, '', [rfReplaceAll]); // 构造data URI格式,替换HTML中的CID var dataUri := 'data:' + att.ContentType + ';base64,' + base64Data; htmlContent := StringReplace(htmlContent, 'cid:' + sContentID, dataUri, [rfReplaceAll]); end; end; end; // 此时htmlContent已可直接用于渲染,包含内嵌的图片data URI // 示例:输出到调试窗口 OutputDebugString(PChar(htmlContent)); finally vMsg.Free; end;
关键步骤说明
- 禁止自动解码:设置
vMsg.NoDecode := True,确保TIdMessage不处理Content-Transfer-Encoding,保留附件的原始Base64字符串。 - 解码HTML内容:HTML部分使用
quoted-printable编码,需用TIdDecoderQuotedPrintable.DecodeString还原正常HTML文本。 - 清理Base64格式:MIME中的Base64会包含换行符,需移除才能生成有效的data URI。
- 替换CID引用:将HTML中
cid:开头的图片引用替换为data:image/xxx;base64,xxx格式,使浏览器无需外部资源即可渲染图片。
内容的提问来源于stack exchange,提问作者Lyn
相关产品推荐
相关产品推荐

