Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 表示邮件消息头部中的键值对。

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消息 IDGet(“Message-ID”)
Received接收路径[“Received”]
Content-Type内容类型Get(“Content-Type”)

RFC 规范

RFC说明
RFC 5322互联网消息格式
RFC 6532SMTPUTF8 扩展
RFC 2047非 ASCII 文本编码
RFC 4155mbox 格式

八、注意事项

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 解码)