
本文详解 mbox 文件格式规范及在 go 中安全、合规地生成 mbox 文件的方法,涵盖 from 行转义、头部与正文结构、空行分隔等关键细节,并提供可直接运行的示例代码。
本文详解 mbox 文件格式规范及在 go 中安全、合规地生成 mbox 文件的方法,涵盖 from 行转义、头部与正文结构、空行分隔等关键细节,并提供可直接运行的示例代码。
mbox 是一种历史悠久但仍在广泛使用的纯文本邮件归档格式,其核心规则简洁却易被忽略:每封邮件必须以形如 From sender@example.com [timestamp] 的特殊分隔行开头(注意是 From 后跟空格,而非邮件头中的 From:);邮件正文内若出现任意以 From 开头的行(即字母 F、r、o、m、空格),必须进行转义(通常 prepend > 或空格);且每封邮件末尾需以一个完全空白行结束。
值得注意的是,Gmail API 返回的邮件数据是标准 RFC 5322 格式(含 From:, Subject: 等头字段),不包含 mbox 所需的 From 分隔行——该行需由写入程序动态生成,并基于真实发件人地址和时间戳构造(例如:From user@gmail.com Mon Jan 1 12:00:00 2024)。因此,完整实现应包含三步:
- 构造并写入 mbox 分隔行(From ...);
- 写入原始邮件头(不含额外空行);
- 写入转义后的邮件正文(逐行检测并处理 From 前缀);
- 最后追加一个空行作为邮件间分隔。
以下是一个生产就绪的 Go 示例,已修正原示例中缺失分隔行、头/体混淆等关键问题:
package main
import (
"fmt"
"io"
"os"
"strings"
"time"
)
// Mail 表示一封解析后的 RFC 5322 邮件(来自 Gmail API 等)
type Mail struct {
From string // 发件人邮箱(用于生成 mbox From 行)
Headers []string // 原始邮件头,如 "From: a@b.com", "Subject: ..."
Body []string // 原始邮件正文行(不含头)
}
// escapeFromLines 对正文行中所有以 "From " 开头的行添加 "> " 前缀
func escapeFromLines(lines []string) []string {
escaped := make([]string, 0, len(lines))
for _, line := range lines {
if strings.HasPrefix(line, "From ") {
escaped = append(escaped, "> "+line)
} else {
escaped = append(escaped, line)
}
}
return escaped
}
// WriteToMbox 将单封邮件写入 io.Writer,符合 mbox 标准
func (m *Mail) WriteToMbox(w io.Writer) error {
// 步骤1:写入 mbox 分隔行(From + 发件人 + 当前时间戳)
ts := time.Now().Format("Mon Jan 2 15:04:05 2006")
fmt.Fprintf(w, "From %s %s\n", m.From, ts)
// 步骤2:写入邮件头(保留原始格式)
for _, h := range m.Headers {
fmt.Fprintln(w, h)
}
fmt.Fprintln(w) // 头部与正文之间需一个空行
// 步骤3:写入转义后的正文
for _, b := range escapeFromLines(m.Body) {
fmt.Fprintln(w, b)
}
// 步骤4:邮件结束,写入空行(分隔下一封)
fmt.Fprintln(w)
return nil
}
// WriteMbox 将多封邮件序列化为 mbox 格式
func WriteMbox(w io.Writer, mails []Mail) error {
for _, m := range mails {
if err := m.WriteToMbox(w); err != nil {
return err
}
}
return nil
}
func main() {
// 模拟从 Gmail API 获取的邮件数据
mail := Mail{
From: "alice@gmail.com",
Headers: []string{
"From: alice@gmail.com",
"To: bob@example.com",
"Subject: Hello from Go",
"Date: Mon, 1 Jan 2024 12:00:00 +0000",
},
Body: []string{
"This is the body.",
"From my notes: important!",
"Another line.",
},
}
// 写入标准输出(可替换为 *os.File)
if err := WriteMbox(os.Stdout, []Mail{mail}); err != nil {
panic(err)
}
}
重要注意事项:
- ✅ 时间戳建议使用邮件原始 Date 头解析后的时间,而非 time.Now(),以保证归档一致性;
- ✅ 若邮件正文含二进制或非 UTF-8 内容,需先 Base64 解码并确保文本化处理;
- ⚠️ 不要将 From: 邮件头误作 mbox 分隔行——二者语义完全不同;
- ⚠️ 转义仅作用于正文行,邮件头中的 From: 行无需、也不应转义;
- ? 实际项目中推荐使用成熟库(如 github.com/emersion/go-mbox)处理边界情况,本教程聚焦原理与最小可行实现。
掌握以上规则与代码结构,即可稳定、可靠地将 Gmail API 或其他来源的邮件数据持久化为标准 mbox 文件,便于后续归档、分析或迁移。











