Go net/mail 包详解
概述
net/mail 包实现了邮件消息的解析功能。该包大部分遵循 RFC 5322 指定的语法,并由 RFC 6532 扩展。
重要说明:
- ✓ 实现 RFC 5322 邮件解析语法
- ✓ 支持 RFC 6532 扩展(UTF-8)
- ✓ 解析邮件地址和头部
- ✓ 支持 RFC 2047 编码名称
- ✓ Go 1.1+ 引入
- ✓ 仅用于解析,不用于发送邮件
主要差异:
- 不解析过时的地址格式(包括嵌入路由信息的地址)
- 不支持完整的空格范围(CFWS 语法元素),如跨行地址
- 不执行 unicode 规范化
- 允许前导 From 行(如 mbox 格式,RFC 4155)
包导入
import (
"net/mail"
)
基本使用
1. 解析邮件地址
package main
import (
"fmt"
"net/mail"
"log"
)
func main() {
// 解析单个地址
address := "Barry Gibbs <bg@example.com>"
addr, err := mail.ParseAddress(address)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Name: %s\n", addr.Name)
fmt.Printf("Address: %s\n", addr.Address)
}
运行结果:
Name: Barry Gibbs
Address: bg@example.com
2. 解析地址列表
package main
import (
"fmt"
"net/mail"
"log"
)
func main() {
list := "Alice <alice@example.com>, Bob <bob@example.com>, eve@example.com"
addresses, err := mail.ParseAddressList(list)
if err != nil {
log.Fatal(err)
}
for _, addr := range addresses {
fmt.Printf("%s <%s>\n", addr.Name, addr.Address)
}
}
运行结果:
Alice <alice@example.com>
Bob <bob@example.com>
Eve <eve@example.com>
3. 解析完整邮件
package main
import (
"fmt"
"net/mail"
"strings"
"log"
"io"
)
func main() {
msg := `From: Gopher <from@example.com>
To: Another Gopher <to@example.com>
Date: Mon, 23 Jun 2015 11:40:36 -0400
Subject: Gophers at Gophercon
Message body`
r := strings.NewReader(msg)
parsedMsg, err := mail.ReadMessage(r)
if err != nil {
log.Fatal(err)
}
// 访问头部
from, err := parsedMsg.Header.Parse("From")
if err != nil {
log.Fatal(err)
}
fmt.Printf("From: %s\n", from)
subject := parsedMsg.Header.Get("Subject")
fmt.Printf("Subject: %s\n", subject)
// 读取正文
body, err := io.ReadAll(parsedMsg.Body)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Body: %s\n", string(body))
}
运行结果:
From: Gopher <from@example.com>
Subject: Gophers at Gophercon
Body: Message body
一、变量
本包没有导出变量。
二、类型(按 a-z 排序)
Address
Address 表示单个邮件地址。诸如“Barry Gibbs bg@example.com“的地址表示为 Address{Name: "Barry Gibbs", Address: "bg@example.com"}。
type Address struct {
Name string // 正确的名称;可以为空
Address string // user@domain
}
字段说明:
Name- 显示名称(可选)Address- 电子邮件地址
ParseAddress
func ParseAddress(address string) (*Address, error)
ParseAddress 解析单个 RFC 5322 地址,例如“Barry Gibbs bg@example.com“。
参数:
address- 地址字符串
返回值:
*Address- 解析后的地址error- 解析错误
示例:
// 带名称的地址
addr, err := mail.ParseAddress("John Doe <john@example.com>")
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s <%s>\n", addr.Name, addr.Address)
// 输出:John Doe <john@example.com>
// 仅电子邮件地址
addr, err = mail.ParseAddress("jane@example.com")
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s <%s>\n", addr.Name, addr.Address)
// 输出: <jane@example.com>
ParseAddressList
func ParseAddressList(list string) ([]*Address, error)
ParseAddressList 将给定的字符串解析为地址列表。
参数:
list- 逗号分隔的地址列表
返回值:
[]*Address- 地址切片error- 解析错误
示例:
list := "Alice <alice@example.com>, Bob <bob@example.com>"
addresses, err := mail.ParseAddressList(list)
if err != nil {
log.Fatal(err)
}
for _, addr := range addresses {
fmt.Printf("%s <%s>\n", addr.Name, addr.Address)
}
Address.String
func (a *Address) String() string
String 将地址格式化为有效的 RFC 5322 地址。如果地址的名称包含非 ASCII 字符,名称将根据 RFC 2047 进行编码。
返回值:
string- 格式化的地址字符串
示例:
addr := &mail.Address{
Name: "John Doe",
Address: "john@example.com",
}
fmt.Println(addr.String())
// 输出:John Doe <john@example.com>
// 带非 ASCII 字符
addr = &mail.Address{
Name: "张三",
Address: "zhangsan@example.com",
}
fmt.Println(addr.String())
// 输出:=?utf-8?b?5byg5Li2?=<zhangsan@example.com>
AddressParser
AddressParser 是 RFC 5322 地址解析器。
type AddressParser struct {
// 包含隐藏或未导出的字段
}
AddressParser.Parse
func (p *AddressParser) Parse(address string) (*Address, error)
Parse 解析单个 RFC 5322 地址,形式为“Gogh Fir gf@example.com“或“foo@example.com”。
参数:
address- 地址字符串
返回值:
*Address- 解析后的地址error- 解析错误
示例:
parser := &mail.AddressParser{}
addr, err := parser.Parse("John Doe <john@example.com>")
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s <%s>\n", addr.Name, addr.Address)
AddressParser.ParseList
func (p *AddressParser) ParseList(list string) ([]*Address, error)
ParseList 将给定的字符串解析为逗号分隔的地址列表,形式为“Gogh Fir gf@example.com“或“foo@example.com”。
参数:
list- 地址列表字符串
返回值:
[]*Address- 地址切片error- 解析错误
示例:
parser := &mail.AddressParser{}
list := "Alice <alice@example.com>, Bob <bob@example.com>"
addresses, err := parser.ParseList(list)
if err != nil {
log.Fatal(err)
}
for _, addr := range addresses {
fmt.Printf("%s <%s>\n", addr.Name, addr.Address)
}
Header
Header 表示邮件消息头部中的键值对。
type Header map[string][]string
说明:
- Header 是 map 类型,键为头部字段名,值为字符串切片
- 每个键可以有多个值
Header.AddressList
func (h Header) AddressList(key string) ([]*Address, error)
AddressList 将命名的头部字段解析为地址列表。
参数:
key- 头部字段名(如“To“、“From”、“Cc”)
返回值:
[]*Address- 地址切片error- 解析错误
示例:
msg, err := mail.ReadMessage(reader)
if err != nil {
log.Fatal(err)
}
// 解析 To 字段
toAddresses, err := msg.Header.AddressList("To")
if err != nil {
log.Fatal(err)
}
for _, addr := range toAddresses {
fmt.Printf("收件人:%s <%s>\n", addr.Name, addr.Address)
}
Header.Date
func (h Header) Date() (time.Time, error)
Date 解析 Date 头部字段。
返回值:
time.Time- 解析后的时间error- 解析错误
示例:
msg, err := mail.ReadMessage(reader)
if err != nil {
log.Fatal(err)
}
date, err := msg.Header.Date()
if err != nil {
log.Fatal(err)
}
fmt.Printf("日期:%s\n", date.Format("2006-01-02 15:04:05"))
Header.Get
func (h Header) Get(key string) string
Get 获取与给定键关联的第一个值。它不区分大小写;使用 CanonicalMIMEHeaderKey 规范化提供的键。如果没有与键关联的值,Get 返回““。
参数:
key- 头部字段名(不区分大小写)
返回值:
string- 第一个值
示例:
msg, err := mail.ReadMessage(reader)
if err != nil {
log.Fatal(err)
}
subject := msg.Header.Get("Subject")
fmt.Printf("主题:%s\n", subject)
// 不区分大小写
subject = msg.Header.Get("subject")
fmt.Printf("主题:%s\n", subject)
Message
Message 表示已解析的邮件消息。
type Message struct {
Header Header // 邮件头部
Body io.Reader // 邮件正文
}
字段说明:
Header- 邮件头部字段Body- 邮件正文读取器
ReadMessage
func ReadMessage(r io.Reader) (msg *Message, err error)
ReadMessage 从 r 读取消息。头部被解析,消息的正文将可从 msg.Body 读取。
参数:
r- io.Reader(包含完整邮件内容)
返回值:
*Message- 解析后的邮件error- 解析错误
示例:
import (
"io"
"net/mail"
"strings"
)
rawMessage := `From: sender@example.com
To: recipient@example.com
Subject: Test Email
Date: Mon, 23 Jun 2015 11:40:36 -0400
This is the body of the email.`
r := strings.NewReader(rawMessage)
msg, err := mail.ReadMessage(r)
if err != nil {
log.Fatal(err)
}
// 访问头部
from := msg.Header.Get("From")
subject := msg.Header.Get("Subject")
// 读取正文
body, err := io.ReadAll(msg.Body)
if err != nil {
log.Fatal(err)
}
fmt.Printf("From: %s\n", from)
fmt.Printf("Subject: %s\n", subject)
fmt.Printf("Body: %s\n", string(body))
三、函数(按 a-z 排序)
ParseDate
func ParseDate(date string) (time.Time, error)
ParseDate 解析 RFC 5322 日期字符串。
参数:
date- RFC 5322 格式的日期字符串
返回值:
time.Time- 解析后的时间error- 解析错误
示例:
package main
import (
"fmt"
"net/mail"
"log"
)
func main() {
dateStr := "Mon, 23 Jun 2015 11:40:36 -0400"
t, err := mail.ParseDate(dateStr)
if err != nil {
log.Fatal(err)
}
fmt.Printf("解析结果:%s\n", t.Format("2006-01-02 15:04:05"))
fmt.Printf("时区:%s\n", t.Location())
}
运行结果:
解析结果:2015-06-23 11:40:36
时区:America/New_York
支持的日期格式:
// RFC 5322 格式
"Mon, 23 Jun 2015 11:40:36 -0400"
"Mon, 23 Jun 2015 15:40:36 +0000"
// 带观察时区
"Mon, 23 Jun 2015 11:40:36 EST"
"Mon, 23 Jun 2015 11:40:36 PST"
// 不带星期
"23 Jun 2015 11:40:36 -0400"
四、典型示例
示例 1:解析各种格式的邮件地址
package main
import (
"fmt"
"net/mail"
"log"
)
func main() {
addresses := []string{
"John Doe <john@example.com>",
"jane@example.com",
"张三 <zhangsan@example.com>",
"\"Smith, John\" <jsmith@example.com>",
"Bob <bob@example.com> (Work)",
}
for _, addrStr := range addresses {
addr, err := mail.ParseAddress(addrStr)
if err != nil {
fmt.Printf("解析失败 %q: %v\n", addrStr, err)
continue
}
fmt.Printf("输入:%q\n", addrStr)
fmt.Printf(" 名称:%q\n", addr.Name)
fmt.Printf(" 地址:%q\n", addr.Address)
fmt.Printf(" 格式化:%q\n\n", addr.String())
}
}
运行结果:
输入:"John Doe <john@example.com>"
名称:"John Doe"
地址:"john@example.com"
格式化:"John Doe <john@example.com>"
输入:"jane@example.com"
名称:""
地址:"jane@example.com"
格式化:"<jane@example.com>"
输入:"张三 <zhangsan@example.com>"
名称:"张三"
地址:"zhangsan@example.com"
格式化:"=?utf-8?b?5byg5Li2?=<zhangsan@example.com>"
输入:"\"Smith, John\" <jsmith@example.com>"
名称:"Smith, John"
地址:"jsmith@example.com"
格式化:"Smith, John <jsmith@example.com>"
示例 2:解析邮件头部
package main
import (
"fmt"
"net/mail"
"strings"
"log"
)
func main() {
rawEmail := `From: Alice <alice@example.com>
To: Bob <bob@example.com>, Charlie <charlie@example.com>
Cc: Dave <dave@example.com>
Subject: Meeting Tomorrow
Date: Tue, 1 Jan 2024 10:00:00 +0800
Message-ID: <12345@example.com>
Hi Bob,
Just a reminder about our meeting tomorrow.
Best regards,
Alice`
r := strings.NewReader(rawEmail)
msg, err := mail.ReadMessage(r)
if err != nil {
log.Fatal(err)
}
// 获取发件人
from, err := msg.Header.AddressList("From")
if err != nil {
log.Fatal(err)
}
fmt.Printf("发件人:%s <%s>\n", from[0].Name, from[0].Address)
// 获取收件人列表
to, err := msg.Header.AddressList("To")
if err != nil {
log.Fatal(err)
}
fmt.Printf("收件人:")
for i, addr := range to {
if i > 0 {
fmt.Print(", ")
}
fmt.Printf("%s <%s>", addr.Name, addr.Address)
}
fmt.Println()
// 获取抄送
cc, err := msg.Header.AddressList("Cc")
if err != nil {
log.Fatal(err)
}
fmt.Printf("抄送:%s <%s>\n", cc[0].Name, cc[0].Address)
// 获取主题
fmt.Printf("主题:%s\n", msg.Header.Get("Subject"))
// 获取日期
date, err := msg.Header.Date()
if err != nil {
log.Fatal(err)
}
fmt.Printf("日期:%s\n", date.Format("2006-01-02 15:04:05"))
// 获取 Message-ID
fmt.Printf("Message-ID: %s\n", msg.Header.Get("Message-ID"))
}
运行结果:
发件人:Alice <alice@example.com>
收件人:Bob <bob@example.com>, Charlie <charlie@example.com>
抄送:Dave <dave@example.com>
主题:Meeting Tomorrow
日期:2024-01-01 10:00:00
Message-ID: <12345@example.com>
示例 3:处理多值头部
package main
import (
"fmt"
"net/mail"
"strings"
)
func main() {
rawEmail := `From: sender@example.com
To: recipient@example.com
Subject: Test
Received: from mail1.example.com by example.com
Received: from mail2.example.com by example.com
Received: from mail3.example.com by example.com
Body`
r := strings.NewReader(rawEmail)
msg, _ := mail.ReadMessage(r)
// 直接访问 map 获取所有 Received 头部
received := msg.Header["Received"]
fmt.Printf("Received 头部数量:%d\n", len(received))
for i, rcvd := range received {
fmt.Printf("%d: %s\n", i+1, rcvd)
}
}
运行结果:
Received 头部数量:3
1: from mail1.example.com by example.com
2: from mail2.example.com by example.com
3: from mail3.example.com by example.com
示例 4:解析带 UTF-8 编码的邮件
package main
import (
"fmt"
"net/mail"
"strings"
"io"
)
func main() {
rawEmail := `From: =?UTF-8?B?5byg5Li2?= <zhangsan@example.com>
To: =?UTF-8?B?5p2l5Lq6?= <lisi@example.com>
Subject: =?UTF-8?B?5L2g5Liq6KaB55qE5YiG?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
你好,
这是一封测试邮件。
祝好,
张三`
r := strings.NewReader(rawEmail)
msg, err := mail.ReadMessage(r)
if err != nil {
fmt.Println(err)
return
}
// 解析发件人(会自动解码 RFC 2047 编码)
from, _ := msg.Header.AddressList("From")
fmt.Printf("发件人:%s <%s>\n", from[0].Name, from[0].Address)
// 解析收件人
to, _ := msg.Header.AddressList("To")
fmt.Printf("收件人:%s <%s>\n", to[0].Name, to[0].Address)
// 获取主题(会自动解码)
fmt.Printf("主题:%s\n", msg.Header.Get("Subject"))
// 读取正文
body, _ := io.ReadAll(msg.Body)
fmt.Printf("正文:\n%s\n", string(body))
}
运行结果:
发件人:张三 <zhangsan@example.com>
收件人:李四 <lisi@example.com>
主题:测试主题
正文:
你好,
这是一封测试邮件。
祝好,
张三
示例 5:验证邮件地址
package main
import (
"fmt"
"net/mail"
"strings"
"regexp"
)
// 简单的电子邮件验证
func isValidEmail(email string) bool {
pattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
matched, _ := regexp.MatchString(pattern, email)
return matched
}
func main() {
addresses := []string{
"valid@example.com",
"invalid.email",
"another@valid.co.uk",
"no-at-sign.com",
"John Doe <john@example.com>",
}
for _, addrStr := range addresses {
addr, err := mail.ParseAddress(addrStr)
if err != nil {
fmt.Printf("%q: 解析失败 - %v\n", addrStr, err)
continue
}
if isValidEmail(addr.Address) {
fmt.Printf("%q: 有效 - %s\n", addrStr, addr.Address)
} else {
fmt.Printf("%q: 无效 - %s\n", addrStr, addr.Address)
}
}
}
运行结果:
"valid@example.com": 有效 - valid@example.com
"invalid.email": 解析失败 - mail: missing '@' or angle-addr
"another@valid.co.uk": 有效 - another@valid.co.uk
"no-at-sign.com": 解析失败 - mail: missing '@' or angle-addr
"John Doe <john@example.com>": 有效 - john@example.com
示例 6:提取邮件中的所有链接
package main
import (
"fmt"
"io"
"net/mail"
"regexp"
"strings"
)
func main() {
rawEmail := `From: sender@example.com
To: recipient@example.com
Subject: Check this out
Hi,
Check out these links:
- https://golang.org
- https://github.com/golang/go
- http://example.com/page
Best regards`
r := strings.NewReader(rawEmail)
msg, err := mail.ReadMessage(r)
if err != nil {
fmt.Println(err)
return
}
// 读取正文
body, err := io.ReadAll(msg.Body)
if err != nil {
fmt.Println(err)
return
}
// 提取 URL
urlPattern := `https?://[^\s]+`
re := regexp.MustCompile(urlPattern)
urls := re.FindAllString(string(body), -1)
fmt.Println("找到的链接:")
for _, url := range urls {
fmt.Println(" -", url)
}
}
运行结果:
找到的链接:
- https://golang.org
- https://github.com/golang/go
- http://example.com/page
示例 7:批量解析收件人
package main
import (
"fmt"
"net/mail"
"strings"
)
func main() {
// 模拟邮件列表
mailingList := []string{
"Alice <alice@example.com>",
"Bob <bob@example.com>",
"Charlie <charlie@example.com>",
"dave@example.com",
}
listStr := strings.Join(mailingList, ", ")
// 解析整个列表
addresses, err := mail.ParseAddressList(listStr)
if err != nil {
fmt.Println(err)
return
}
// 按域名分组
domainMap := make(map[string][]*mail.Address)
for _, addr := range addresses {
parts := strings.Split(addr.Address, "@")
if len(parts) == 2 {
domain := parts[1]
domainMap[domain] = append(domainMap[domain], addr)
}
}
// 输出结果
for domain, addrs := range domainMap {
fmt.Printf("\n域名:%s (%d 个地址)\n", domain, len(addrs))
for _, addr := range addrs {
if addr.Name != "" {
fmt.Printf(" %s <%s>\n", addr.Name, addr.Address)
} else {
fmt.Printf(" %s\n", addr.Address)
}
}
}
}
运行结果:
域名:example.com (4 个地址)
Alice <alice@example.com>
Bob <bob@example.com>
Charlie <charlie@example.com>
dave@example.com
示例 8:解析 mbox 格式邮件
package main
import (
"bufio"
"fmt"
"net/mail"
"strings"
"io"
)
func main() {
// mbox 格式(以 From 行开头)
mboxData := `From sender@example.com Mon Jan 1 10:00:00 2024
From: sender@example.com
To: recipient@example.com
Subject: Test Email
Date: Mon, 1 Jan 2024 10:00:00 +0800
Email body here.
From another@example.com Mon Jan 1 11:00:00 2024
From: another@example.com
To: recipient@example.com
Subject: Second Email
Date: Mon, 1 Jan 2024 11:00:00 +0800
Second email body.`
scanner := bufio.NewScanner(strings.NewReader(mboxData))
var currentEmail strings.Builder
inEmail := false
for scanner.Scan() {
line := scanner.Text()
// 检测 mbox 的 From 行
if strings.HasPrefix(line, "From ") && inEmail {
// 处理前一封邮件
if currentEmail.Len() > 0 {
processEmail(currentEmail.String())
}
currentEmail.Reset()
inEmail = true
} else if strings.HasPrefix(line, "From ") && !inEmail {
inEmail = true
} else if inEmail {
currentEmail.WriteString(line)
currentEmail.WriteString("\n")
}
}
// 处理最后一封邮件
if currentEmail.Len() > 0 {
processEmail(currentEmail.String())
}
}
func processEmail(rawEmail string) {
r := strings.NewReader(rawEmail)
msg, err := mail.ReadMessage(r)
if err != nil {
fmt.Println("解析错误:", err)
return
}
fmt.Printf("主题:%s\n", msg.Header.Get("Subject"))
fmt.Printf("发件人:%s\n", msg.Header.Get("From"))
body, _ := io.ReadAll(msg.Body)
fmt.Printf("正文:%s\n", strings.TrimSpace(string(body)))
fmt.Println("---")
}
运行结果:
主题:Test Email
发件人:sender@example.com
正文:Email body here.
---
主题:Second Email
发件人:another@example.com
正文:Second email body.
---
五、最佳实践
1. 正确处理解析错误
addr, err := mail.ParseAddress(address)
if err != nil {
// 详细错误处理
log.Printf("解析地址 %q 失败:%v", address, err)
// 可以跳过或使用备用地址
return
}
2. 使用 AddressList 解析多个地址
// ✓ 推荐 - 使用 AddressList
toAddresses, err := header.AddressList("To")
if err != nil {
log.Fatal(err)
}
// ✗ 不推荐 - 手动分割
toStr := header.Get("To")
addresses := strings.Split(toStr, ",") // 可能出错
3. 处理 RFC 2047 编码
// net/mail 自动处理 RFC 2047 编码
// 无需手动解码
msg, _ := mail.ReadMessage(reader)
subject := msg.Header.Get("Subject")
// 如果主题是 =?UTF-8?B?...?= 格式,会自动解码
4. 直接访问 Header map 获取多值
// 获取所有 Received 头部
received := msg.Header["Received"]
for _, rcvd := range received {
fmt.Println(rcvd)
}
// 或使用 Get 获取第一个值
first := msg.Header.Get("Received")
5. 日期解析错误处理
date, err := header.Date()
if err != nil {
// 日期格式可能不正确
log.Printf("日期解析失败:%v", err)
// 可以使用当前时间或其他默认值
date = time.Now()
}
6. 处理大型邮件
// 使用 LimitReader 限制读取大小
import "io"
limitedReader := io.LimitReader(reader, 10*1024*1024) // 10MB
msg, err := mail.ReadMessage(limitedReader)
六、与其他包配合
1. 与 mime/multipart 配合解析带附件的邮件
import (
"mime/multipart"
"net/mail"
)
msg, err := mail.ReadMessage(reader)
if err != nil {
log.Fatal(err)
}
// 检查 Content-Type
contentType := msg.Header.Get("Content-Type")
if strings.HasPrefix(contentType, "multipart/") {
mr, err := multipart.NewReader(msg.Body, getBoundary(contentType))
if err != nil {
log.Fatal(err)
}
for {
part, err := mr.NextPart()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
// 处理每个部分
if part.FileName() != "" {
// 这是附件
fmt.Println("附件:", part.FileName())
} else {
// 这是正文
body, _ := io.ReadAll(part)
fmt.Println("正文:", string(body))
}
}
}
2. 与 encoding/base64 配合解码
import (
"encoding/base64"
"net/mail"
)
msg, _ := mail.ReadMessage(reader)
// 检查 Content-Transfer-Encoding
encoding := msg.Header.Get("Content-Transfer-Encoding")
if encoding == "base64" {
body, _ := io.ReadAll(msg.Body)
decoded, err := base64.StdEncoding.DecodeString(string(body))
if err != nil {
log.Fatal(err)
}
fmt.Println(string(decoded))
}
3. 与 time 配合格式化日期
import (
"net/mail"
"time"
)
msg, _ := mail.ReadMessage(reader)
date, err := msg.Header.Date()
if err != nil {
log.Fatal(err)
}
// 格式化为不同格式
fmt.Println(date.Format("2006-01-02")) // 2024-01-01
fmt.Println(date.Format("Jan 2, 2006")) // Jan 1, 2024
fmt.Println(date.Format(time.RFC3339)) // 2024-01-01T10:00:00+08:00
七、快速参考
类型总览
| 类型 | 说明 |
|---|---|
| Address | 邮件地址(Name + Address) |
| AddressParser | 地址解析器 |
| Header | 邮件头部 map |
| Message | 解析后的邮件(Header + Body) |
函数总览
| 函数 | 说明 |
|---|---|
| ParseAddress | 解析单个地址 |
| ParseAddressList | 解析地址列表 |
| ParseDate | 解析 RFC 5322 日期 |
| ReadMessage | 读取并解析完整邮件 |
Address 方法
| 方法 | 说明 |
|---|---|
| String() | 格式化为 RFC 5322 地址 |
AddressParser 方法
| 方法 | 说明 |
|---|---|
| Parse | 解析单个地址 |
| ParseList | 解析地址列表 |
Header 方法
| 方法 | 说明 |
|---|---|
| AddressList | 解析地址列表字段 |
| Date | 解析 Date 字段 |
| Get | 获取字段值(第一个) |
常用头部字段
| 字段 | 说明 | 解析方法 |
|---|---|---|
| From | 发件人 | AddressList(“From”) |
| To | 收件人 | AddressList(“To”) |
| Cc | 抄送 | AddressList(“Cc”) |
| Bcc | 密送 | AddressList(“Bcc”) |
| Subject | 主题 | Get(“Subject”) |
| Date | 日期 | Date() |
| Message-ID | 消息 ID | Get(“Message-ID”) |
| Received | 接收路径 | [“Received”] |
| Content-Type | 内容类型 | Get(“Content-Type”) |
RFC 规范
| RFC | 说明 |
|---|---|
| RFC 5322 | 互联网消息格式 |
| RFC 6532 | SMTPUTF8 扩展 |
| RFC 2047 | 非 ASCII 文本编码 |
| RFC 4155 | mbox 格式 |
八、注意事项
1. 仅支持解析,不支持发送
// net/mail 只用于解析邮件
// 发送邮件使用 net/smtp 包
// ✓ 正确
msg, err := mail.ReadMessage(reader)
// ✗ 错误 - net/mail 不能发送邮件
mail.Send(msg) // 不存在
2. 不解析过时格式
// 以下过时格式不被支持:
// - 带路由信息的地址:<@host1,@host2:user@domain>
// - 跨行地址(CFWS)
// - 注释嵌入地址
// ✓ 支持
"John Doe <john@example.com>"
// ✗ 不支持(会报错)
"<@host1,@host2:user@domain>"
3. Header Get 不区分大小写
header := mail.Header{"Subject": []string{"Test"}}
// 以下都返回相同结果
header.Get("Subject") // "Test"
header.Get("subject") // "Test"
header.Get("SUBJECT") // "Test"
4. 多值头部直接访问 map
// Get 只返回第一个值
header := mail.Header{
"Received": []string{"first", "second", "third"},
}
header.Get("Received") // "first"
header["Received"] // ["first", "second", "third"]
5. RFC 2047 自动解码
// net/mail 自动解码 RFC 2047 编码
// =?UTF-8?B?5byg5Li2?= -> "张三"
msg, _ := mail.ReadMessage(reader)
subject := msg.Header.Get("Subject")
// 如果编码,会自动解码
6. Body 只能读取一次
msg, err := mail.ReadMessage(reader)
if err != nil {
log.Fatal(err)
}
// 第一次读取
body1, _ := io.ReadAll(msg.Body)
// 第二次读取会返回空
body2, _ := io.ReadAll(msg.Body) // 空
7. 地址格式化
addr := &mail.Address{
Name: "John Doe",
Address: "john@example.com",
}
// String 自动格式化
fmt.Println(addr.String())
// 输出:John Doe <john@example.com>
// 非 ASCII 名称自动编码
addr.Name = "张三"
fmt.Println(addr.String())
// 输出:=?utf-8?b?5byg5Li2?=<john@example.com>
8. 日期格式容错
// ParseDate 支持多种格式
dates := []string{
"Mon, 23 Jun 2015 11:40:36 -0400", // 标准格式
"23 Jun 2015 11:40:36 -0400", // 无星期
"Mon, 23 Jun 2015 11:40:36 EST", // 时区名称
}
for _, dateStr := range dates {
t, err := mail.ParseDate(dateStr)
if err != nil {
log.Printf("解析失败 %q: %v", dateStr, err)
}
}
最后更新: 2026-04-05
Go 版本: Go 1.1+
包文档: https://pkg.go.dev/net/mail
相关 RFC: RFC 5322 (Internet Message Format), RFC 6532 (SMTPUTF8), RFC 2047 (Encoded Words)
相关包: net/smtp(发送邮件), mime/multipart(多部分消息), encoding/base64(Base64 解码)