如何使用Golang调用Gmail API实现跨账号邮件发送
Golang 调用 Gmail API 实现个人账号发信可行方案
前置准备
- 进入Google Cloud控制台创建新项目,在API库中搜索启用Gmail API
- 创建OAuth 2.0客户端凭证,应用类型选择「桌面应用」,下载生成的凭证文件保存为
credentials.json放到项目目录 - 权限范围仅申请
https://www.googleapis.com/auth/gmail.send即可,该权限仅允许发信,不会触发高权限的安全审核,个人使用可以直接通过
依赖安装
执行命令安装官方SDK和依赖:
go get google.golang.org/api/gmail/v1 go get golang.org/x/oauth2 go get golang.org/x/oauth2/google
核心实现说明
*关键踩坑点提前说明:Gmail发信接口要求传入的邮件内容必须使用**URL安全的无填充base64(RawURLEncoding)*编码,使用标准base64会直接返回400错误;邮件头必须严格遵循RFC 822规范,换行统一使用\r\n。
完整可运行代码如下:
package main import ( "context" "encoding/base64" "encoding/json" "fmt" "io/ioutil" "log" "net/mail" "os" "golang.org/x/oauth2" "golang.org/x/oauth2/google" "google.golang.org/api/gmail/v1" "google.golang.org/api/option" ) // 读取本地凭证初始化Gmail服务客户端 func getGmailService() (*gmail.Service, error) { b, err := ioutil.ReadFile("credentials.json") if err != nil { return nil, fmt.Errorf("读取credentials.json失败: %w", err) } // 仅配置发信权限 config, err := google.ConfigFromJSON(b, gmail.GmailSendScope) if err != nil { return nil, fmt.Errorf("解析凭证配置失败: %w", err) } tokFile := "token.json" tok, err := tokenFromFile(tokFile) if err != nil { tok = getTokenFromWeb(config) saveToken(tokFile, tok) } client := config.Client(context.Background(), tok) srv, err := gmail.NewService(context.Background(), option.WithHTTPClient(client)) if err != nil { return nil, fmt.Errorf("创建Gmail服务失败: %w", err) } return srv, nil } // 走浏览器授权流程获取token func getTokenFromWeb(config *oauth2.Config) *oauth2.Token { authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline) fmt.Printf("请打开以下链接完成授权: \n%v\n", authURL) fmt.Print("输入授权后页面返回的授权码: ") var authCode string if _, err := fmt.Scan(&authCode); err != nil { log.Fatalf("读取授权码失败: %v", err) } tok, err := config.Exchange(context.TODO(), authCode) if err != nil { log.Fatalf("换取token失败: %v", err) } return tok } // 从本地文件读取已保存的token func tokenFromFile(file string) (*oauth2.Token, error) { f, err := os.Open(file) if err != nil { return nil, err } defer f.Close() tok := &oauth2.Token{} err = json.NewDecoder(f).Decode(tok) return tok, err } // 持久化token到本地 func saveToken(path string, token *oauth2.Token) { fmt.Printf("保存凭证到: %s\n", path) f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { log.Fatalf("无法写入token文件: %v", err) } defer f.Close() json.NewEncoder(f).Encode(token) } // 发信方法 func SendEmail(to, subject, body string) error { srv, err := getGmailService() if err != nil { return err } // 构造RFC 822标准格式邮件 from := mail.Address{Name: "发件人名称", Address: "你的Gmail邮箱地址@gmail.com"} toAddr := mail.Address{Address: to} msg := fmt.Sprintf("From: %s\r\nTo: %s\r\nSubject: %s\r\nContent-Type: text/plain; charset=\"UTF-8\"\r\n\r\n%s", from.String(), toAddr.String(), subject, body) // 必须使用RawURLEncoding编码 rawMsg := base64.RawURLEncoding.EncodeToString([]byte(msg)) gmailMsg := &gmail.Message{ Raw: rawMsg, } // me代表当前授权的账号 _, err = srv.Users.Messages.Send("me", gmailMsg).Do() if err != nil { return fmt.Errorf("调用发信接口失败: %w", err) } return nil } func main() { err := SendEmail("收件人邮箱@xxx.com", "测试邮件标题", "这是通过Golang调用Gmail API发送的测试邮件内容") if err != nil { log.Fatalf("发信失败: %v", err) } log.Println("邮件发送成功") }
常见问题排查
- 首次运行程序会自动输出授权链接,复制到浏览器登录你的Gmail账号完成授权,将页面返回的授权码粘贴回终端即可,授权成功后会在本地生成
token.json保存凭证,后续运行不需要重复授权 - 报错
invalid_grant:先检查本地系统时间是否和网络时间同步,无效的话删除token.json重新走授权流程即可 - 报错
403 Forbidden:确认Gmail API已经在云控制台启用,OAuth凭证类型选的是桌面应用,权限范围没有配置错误 - 报错
400 Bad Request:优先检查邮件编码方式是否正确,邮件头格式是否符合规范 - 需要发送HTML邮件时,仅需把Content-Type改为
text/html; charset="UTF-8",正文传入HTML代码即可;需要添加附件的话构造multipart格式邮件,编码规则保持一致即可
内容的提问来源于stack exchange,提问作者Bryan Ho
相关产品推荐
相关产品推荐

