fiber 默认不内置邮件能力,fiber.new() 创建的 app 实例无 sendmail 方法,需用 net/smtp 或 gomail 自行封装并复用 smtp 客户端,敏感配置应从环境变量读取,且 from 头须与认证账号一致。

为什么 fiber.New() 后直接调用邮件发送会失败
因为 Fiber 默认不内置邮件能力,fiber.New() 创建的 app 实例本身没有 SendMail 方法。你看到的“发送邮件”逻辑必须自己封装,常见做法是用 net/smtp 或第三方库(如 gomail)构建独立服务,并在路由中调用。
典型错误是试图往 app 上挂方法,或误以为 fiber.Context 提供了邮件接口——它只管 HTTP 生命周期。
- 邮件发送必须显式初始化 SMTP 客户端,且建议复用连接(避免每请求都
smtp.Dial) - 敏感配置(如邮箱密码、SMTP 地址)不要硬编码,应从环境变量读取,例如
os.Getenv("SMTP_PASSWORD") - 如果用
gomail,注意其SetHeader("From")和实际 SMTP 认证账号需一致,否则 530 错误
如何用 ctx.FormFile("file") 正确接收上传的附件
Fiber 的文件上传依赖 ctx.FormFile,但它只解析 multipart/form-data 请求体中的单个字段。如果你前端用 fetch 发送 FormData,后端却用 ctx.Body() 读原始字节,就拿不到文件。
常见陷阱是混淆字段名:前端 append("attachment", file),后端就必须用 ctx.FormFile("attachment"),而不是写死成 "file"。
- 上传前务必检查
err:若file, err := ctx.FormFile("attachment")返回非 nil 错误,可能是字段名错、请求头缺失Content-Type: multipart/form-data,或没加enctype="multipart/form-data" - 临时文件路径由 Fiber 自动管理,但
file.Filename是客户端传来的原始名,不可直接拼进os.Open——需校验是否含../防路径遍历 - 大文件建议加大小限制:
app.Use(func(c *fiber.Ctx) error { c.Locals("maxMultipartMemory", 10
附件怎么塞进邮件里而不爆内存
别把整个附件读进内存再传给邮件库。Fiber 的 file.Open() 返回 io.ReadSeeker,而 gomail 的 Attach 方法支持直接传入该接口,内部流式读取。
错误做法是先 os.ReadFile(file.Filename) 再 SetBody —— 这会让 10MB 文件占 20MB 内存(一份在磁盘缓存,一份在 Go 变量)。
- 正确链路:
file, _ := ctx.FormFile("attachment")→f, _ := file.Open()→m.Attach(file.Filename, gomail.SetCopyFunc(f)) - 记得关文件句柄:
defer f.Close(),否则并发上传多文件时可能触发too many open files - 如果附件类型未知,用
file.Header.Get("Content-Type")获取 MIME 类型,比靠扩展名更可靠
ctx.SendStatus(500) 之前忘了 return 导致邮件发了两次
Fiber 路由函数执行完才结束响应,但很多开发者在错误分支里写 ctx.SendStatus(500) 就继续往下走,结果后续代码仍执行(比如又调了一次 sendEmail()),造成重复发送或 panic。
这是 Go + Fiber 组合里最隐蔽的坑之一:HTTP 响应已发出,但 Goroutine 还在跑。
- 所有提前退出分支必须显式
return,哪怕只是return ctx.SendStatus(400) - 不要依赖
log.Fatal或os.Exit来中断流程——它们杀整个进程,不是当前请求 - 复杂逻辑建议用
if err != nil { return handleError(ctx, err) }统一封装,避免漏return
附件路径校验、SMTP 连接超时、邮件模板渲染失败——这些地方最容易忘记 return,一漏就是生产事故。











