用gomail发送带附件邮件最稳妥,因其自动处理mime边界、base64编码及头字段,避免手写rfc合规问题;调用m.attach()即可添加本地文件,内存数据需手动addheader并base64编码。

用 gomail 发送带附件的邮件最稳妥
Go 标准库没有直接支持 MIME 多部分(如附件)的高层邮件发送接口,net/smtp 只能发纯文本。硬手写 MIME 结构极易出错——边界符不匹配、编码缺失、换行符平台差异都会导致附件乱码或收不到。实际项目中,gomail(gopkg.in/gomail.v2)是目前最轻量且维护良好的选择,它自动处理 Content-Type、Content-Transfer-Encoding 和边界生成。
安装:
go get gopkg.in/gomail.v2
关键点:
-
gomail.NewMessage()默认使用multipart/mixed,天然适配正文 + 附件组合 - 调用
m.Attach("path/to/file.pdf")会自动读取文件、设置Content-Disposition: attachment和合适的Content-Type(基于文件扩展名) - 如果附件路径含中文或空格,务必先用
filepath.Abs()或确保路径已正确转义,否则Attach()会静默失败(无错误返回,但附件不出现)
Attach() 不支持内存字节流?用 AddHeader 手动构造
当附件来自 HTTP 响应体、数据库 BLOB 或加密解密后的 []byte 时,Attach() 无法直接使用。此时需绕过文件系统,手动添加 MIME 部分:
示例:附加一个动态生成的 CSV 字节切片
csvData := []byte("name,age\nAlice,30\nBob,25")
m := gomail.NewMessage()
m.SetHeader("From", "me@example.com")
m.SetHeader("To", "you@example.com")
m.SetHeader("Subject", "Report")
m.SetBody("text/plain", "See attached report.")
// 手动添加附件部分
m.AddHeader("Content-Type", "text/csv; charset=utf-8; name=\"report.csv\"")
m.AddHeader("Content-Disposition", "attachment; filename=\"report.csv\"")
m.AddHeader("Content-Transfer-Encoding", "base64")
m.SetBody("text/csv", base64.StdEncoding.EncodeToString(csvData))
注意:
-
AddHeader()必须在SetBody()之前调用,否则会被覆盖 -
Content-Transfer-Encoding强烈建议用base64,避免二进制数据被 SMTP 中继破坏;quoted-printable仅适合 ASCII 主体 - 文件名含中文时,
filename=后面不能直接写 UTF-8 字节,应使用 RFC 2231 编码(filename*=UTF-8''%E6%8A%A5%E5%91%8A.csv),但多数邮箱客户端兼容直接 UTF-8,可先试;不兼容时再上编码库
SMTP 认证失败或连接超时?检查 gomail.Dialer 的配置顺序
常见错误信息:failed to authenticate: 535 5.7.8 Username and Password not accepted 或 dial tcp: i/o timeout。根本原因常是 Dialer 初始化参数顺序或值不匹配:
- 端口必须与加密方式严格对应:Gmail 用
587+StartTLS,Outlook 用587,而某些企业 SMTP 要求465+ SSL(即SSL: true) -
gomail.NewDialer()的第四个参数(密码)不能为nil或空字符串,即使 SMTP 服务允许无密码(极少见),也要传"" - Gmail 等服务已禁用「低安全性应用」,必须使用「应用专用密码」而非账户密码;开启两步验证后,在 Google 账户安全页生成
- 本地开发时若用
localhost:1025(如 MailHog),记得设SSL: false且不启用StartTLS
附件打开提示损坏?重点检查 Content-Type 和换行符
用户下载附件后双击打不开,或提示“文件已损坏”,大概率是 MIME 结构被破坏。最隐蔽的原因是:Go 字符串默认用 \n 换行,但 SMTP 协议要求 CRLF(\r\n)。gomail 内部已修正此问题,但如果你混用了手动拼接字符串和 gomail 的 API,就可能引入错误。
自查清单:
- 不要用
fmt.Sprintf拼接整个 MIME 消息体——这是最常踩的坑 - 所有附件内容(尤其是图片、PDF)必须经过
base64编码后再塞入SetBody(),不可直接传原始[]byte - 用
curl -v或 Wireshark 抓包看实际发出的邮件原始内容,确认每个boundary=...是否唯一且成对出现,最后一段 boundary 后是否有额外-- - 测试时优先用 Gmail/Outlook 收件,它们对 MIME 容错性较低,比国内某些客户端更能暴露问题
附件逻辑本身不复杂,但 MIME 规范细节多、错误反馈弱。宁可多花十分钟验证原始邮件头,也不要靠猜。











