需手动调用mail.decodeword解码header中文字段,对to等复合字段应先parseaddresslist再解码name;readmessage要求严格rfc 5322格式;提取邮箱必须用parseaddresslist;multipart邮件需递归解析parts并按content-transfer-encoding解码。

解析 net/mail 的 Header 时,中文字段乱码怎么办
Go 的 net/mail 默认不自动解码 MIME-encoded-word(比如 =?UTF-8?B?5L2g5aW9?=),直接读 Header.Get("Subject") 会拿到原始编码字符串,不是乱码,是“没解”。
- 必须手动调用
mail.DecodeWord处理每个字段值,不能只 decode 一次——Header里一个 key 可能对应多个 value,且每个 value 内部可能含多个 encoded-word - 常见错误:对整个
Header.Get("To")字符串直接DecodeWord,但实际 To 字段格式是"Name <addr>"</addr>,只有 Name 部分需解码,邮箱部分不能动 - 推荐做法:用
mail.ParseAddressList先拆出*mail.Address,再对address.Name单独 decode;Subject、Cc 等纯文本字段可整字段 decode
mail.ReadMessage 读到空 Header 或 panic 怎么办
这个函数要求输入是完整的 RFC 5322 格式邮件(含空行分隔头与体),少一个空行、多一个 BOM、开头有空白,都可能导致 Header 解析失败或返回 nil。
- 检查输入 reader 是否从第一行开始 —— 如果是文件,确认没用
os.Open后直接传给ReadMessage,而应确保 reader 位置在开头;如果是网络流,注意是否有前置协议头干扰 - 常见错误:把 raw HTTP 响应体(含
HTTP/1.1 200 OK\r\n...)直接喂给ReadMessage,它根本不是邮件格式 - 调试技巧:先用
io.ReadAll把前 512 字节打印出来,看是否以From:、Received:或Subject:开头,且头尾之间有且仅有一个\r\n\r\n
提取发件人邮箱时,Header.Get("From") 和 mail.ParseAddressList 哪个更可靠
Header.Get("From") 只返回原始字符串,不可直接当邮箱用;真正提取结构化地址必须过 mail.ParseAddressList。
-
ParseAddressList能正确处理带引号的名字、含逗号的姓名、嵌套括号等边界情况,比如"Zhang, San" <san></san>或user+tag@example.com - 错误用法:对
Header.Get("From")做strings.Split(..., "@")—— 一旦名字里有 @(如"test@company" <me></me>),就切歪了 - 注意:该函数返回
[]*mail.Address,即使 From 只有一个地址,也要取[0].Address,别漏掉索引
为什么用 net/mail 解析带附件的邮件正文总是不全
net/mail 只负责解析邮件头和顶层结构,它不递归解析 multipart body,也不解码 base64 / quoted-printable。所谓“正文不全”,其实是你没往下钻进 multipart/alternative 或 multipart/mixed 的 parts 里。
立即学习“go语言免费学习笔记(深入)”;
- 判断是否 multipart:检查
Header.Get("Content-Type")是否包含multipart/,再用mime.ParseMediaType提取boundary - 不要自己手写 boundary 分割逻辑 —— 用
mail.ReadMIMEHeader+mime.WordDecoder+ 第三方库如github.com/emersion/go-message更稳妥 -
net/mail本身不提供 body 解码能力,base64 内容得用base64.StdEncoding.DecodeString,quoted-printable 得用mime.QEncoding.DecodeString
最常被忽略的一点:邮件头里的 Content-Transfer-Encoding 和 body 实际编码可能不一致,尤其老邮件,得按 header 声明的方式解,不能猜。










