使用golang发送带附件的电子邮件

huangapple go评论119阅读模式
英文:

Send email with attachments in golang

问题

这是代码:

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "log"
  6. "net/http"
  7. "net/url"
  8. "os"
  9. "os/user"
  10. "path/filepath"
  11. "golang.org/x/net/context"
  12. "golang.org/x/oauth2"
  13. "golang.org/x/oauth2/google"
  14. "google.golang.org/api/gmail/v1"
  15. "encoding/base64"
  16. "io/ioutil"
  17. )
  18. // getClient 使用上下文和配置来检索令牌,然后生成客户端。它返回生成的客户端。
  19. func getClient(ctx context.Context, config *oauth2.Config, configFileName string) *http.Client {
  20. cacheFile, err := tokenCacheFile(configFileName)
  21. if err != nil {
  22. log.Fatalf("无法获取缓存凭据文件的路径。%v", err)
  23. }
  24. tok, err := tokenFromFile(cacheFile)
  25. if err != nil {
  26. tok = getTokenFromWeb(config)
  27. saveToken(cacheFile, tok)
  28. }
  29. return config.Client(ctx, tok)
  30. }
  31. // getTokenFromWeb 使用配置来请求令牌。它返回检索到的令牌。
  32. func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
  33. authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
  34. fmt.Printf("在浏览器中打开以下链接,然后输入授权码:\n%v\n", authURL)
  35. var code string
  36. if _, err := fmt.Scan(&code); err != nil {
  37. log.Fatalf("无法读取授权码 %v", err)
  38. }
  39. tok, err := config.Exchange(oauth2.NoContext, code)
  40. if err != nil {
  41. log.Fatalf("无法从网络中检索令牌 %v", err)
  42. }
  43. return tok
  44. }
  45. // tokenCacheFile 生成凭据文件的路径/文件名。它返回生成的凭据路径/文件名。
  46. func tokenCacheFile(filename string) (string, error) {
  47. usr, err := user.Current()
  48. if err != nil {
  49. return "", err
  50. }
  51. tokenCacheDir := filepath.Join(usr.HomeDir, ".credentials")
  52. os.MkdirAll(tokenCacheDir, 0700)
  53. return filepath.Join(tokenCacheDir,
  54. url.QueryEscape(filename)), err
  55. }
  56. // tokenFromFile 从给定的文件路径检索令牌。它返回检索到的令牌和遇到的任何读取错误。
  57. func tokenFromFile(file string) (*oauth2.Token, error) {
  58. f, err := os.Open(file)
  59. if err != nil {
  60. return nil, err
  61. }
  62. t := &oauth2.Token{}
  63. err = json.NewDecoder(f).Decode(t)
  64. defer f.Close()
  65. return t, err
  66. }
  67. // saveToken 使用文件路径创建文件并将令牌存储在其中。
  68. func saveToken(file string, token *oauth2.Token) {
  69. fmt.Printf("将凭据文件保存到:%s\n", file)
  70. f, err := os.Create(file)
  71. if err != nil {
  72. log.Fatalf("无法缓存OAuth令牌:%v", err)
  73. }
  74. defer f.Close()
  75. json.NewEncoder(f).Encode(token)
  76. }
  77. func main() {
  78. ctx := context.Background()
  79. b, err := ioutil.ReadFile("client_secret.json")
  80. if err != nil {
  81. log.Fatalf("无法读取客户端密钥文件:%v", err)
  82. }
  83. sendConfig, err := google.ConfigFromJSON(b, gmail.GmailSendScope)
  84. if err != nil {
  85. log.Fatalf("无法解析客户端密钥文件为配置:%v", err)
  86. }
  87. sendClient := getClient(ctx, sendConfig, "send.json")
  88. sendService, err := gmail.New(sendClient)
  89. if err != nil {
  90. log.Fatalf("无法检索Gmail客户端:%v", err)
  91. }
  92. if err := SendEmail(ctx, sendService, "jane1988@gmail.com"); err != nil {
  93. log.Fatalf("发送邮件失败:%v", err)
  94. }
  95. }
  96. func SendEmail(ctx context.Context, svc *gmail.Service, email string) error {
  97. header := make(map[string]string)
  98. header["To"] = email
  99. header["Subject"] = "你好"
  100. header["MIME-Version"] = "1.0"
  101. header["Content-Type"] = `text/html; charset="utf-8"`
  102. header["Content-Transfer-Encoding"] = "base64"
  103. var msg string
  104. for k, v := range header {
  105. msg += fmt.Sprintf("%s: %s\n", k, v)
  106. }
  107. msg += "\n" + "你好,Gmail!"
  108. gmsg := gmail.Message{
  109. Raw: encodeWeb64String([]byte(msg)),
  110. }
  111. _, err := svc.Users.Messages.Send("me", &gmsg).Do()
  112. return err
  113. }
  114. func encodeWeb64String(b []byte) string {
  115. s := base64.URLEncoding.EncodeToString(b)
  116. var i = len(s) - 1
  117. for s[i] == '=' {
  118. i--
  119. }
  120. return s[0 : i+1]
  121. }

这段代码可以完美运行,但是没有附件。你想知道如何将文件附加到邮件中。

英文:

Here is the code:

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "log"
  6. "net/http"
  7. "net/url"
  8. "os"
  9. "os/user"
  10. "path/filepath"
  11. "golang.org/x/net/context"
  12. "golang.org/x/oauth2"
  13. "golang.org/x/oauth2/google"
  14. "google.golang.org/api/gmail/v1"
  15. "encoding/base64"
  16. "io/ioutil"
  17. )
  18. // getClient uses a Context and Config to retrieve a Token
  19. // then generate a Client. It returns the generated Client.
  20. func getClient(ctx context.Context, config *oauth2.Config, configFileName string) *http.Client {
  21. cacheFile, err := tokenCacheFile(configFileName)
  22. if err != nil {
  23. log.Fatalf("Unable to get path to cached credential file. %v", err)
  24. }
  25. tok, err := tokenFromFile(cacheFile)
  26. if err != nil {
  27. tok = getTokenFromWeb(config)
  28. saveToken(cacheFile, tok)
  29. }
  30. return config.Client(ctx, tok)
  31. }
  32. // getTokenFromWeb uses Config to request a Token.
  33. // It returns the retrieved Token.
  34. func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
  35. authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
  36. fmt.Printf("Go to the following link in your browser then type the " +
  37. "authorization code: \n%v\n", authURL)
  38. var code string
  39. if _, err := fmt.Scan(&code); err != nil {
  40. log.Fatalf("Unable to read authorization code %v", err)
  41. }
  42. tok, err := config.Exchange(oauth2.NoContext, code)
  43. if err != nil {
  44. log.Fatalf("Unable to retrieve token from web %v", err)
  45. }
  46. return tok
  47. }
  48. // tokenCacheFile generates credential file path/filename.
  49. // It returns the generated credential path/filename.
  50. func tokenCacheFile(filename string) (string, error) {
  51. usr, err := user.Current()
  52. if err != nil {
  53. return "", err
  54. }
  55. tokenCacheDir := filepath.Join(usr.HomeDir, ".credentials")
  56. os.MkdirAll(tokenCacheDir, 0700)
  57. return filepath.Join(tokenCacheDir,
  58. url.QueryEscape(filename)), err
  59. }
  60. // tokenFromFile retrieves a Token from a given file path.
  61. // It returns the retrieved Token and any read error encountered.
  62. func tokenFromFile(file string) (*oauth2.Token, error) {
  63. f, err := os.Open(file)
  64. if err != nil {
  65. return nil, err
  66. }
  67. t := &oauth2.Token{}
  68. err = json.NewDecoder(f).Decode(t)
  69. defer f.Close()
  70. return t, err
  71. }
  72. // saveToken uses a file path to create a file and store the
  73. // token in it.
  74. func saveToken(file string, token *oauth2.Token) {
  75. fmt.Printf("Saving credential file to: %s\n", file)
  76. f, err := os.Create(file)
  77. if err != nil {
  78. log.Fatalf("Unable to cache oauth token: %v", err)
  79. }
  80. defer f.Close()
  81. json.NewEncoder(f).Encode(token)
  82. }
  83. func main() {
  84. // Use oauth2.NoContext if there isn't a good context to pass in.
  85. //ctx := context.TODO()
  86. ctx := context.Background()
  87. b, err := ioutil.ReadFile("client_secret.json")
  88. if err != nil {
  89. log.Fatalf("Unable to read client secret file: %v", err)
  90. }
  91. // If modifying these scopes, delete your previously saved credentials
  92. // at ~/.credentials/gmail-go-quickstart.json
  93. sendConfig, err := google.ConfigFromJSON(b, gmail.GmailSendScope)
  94. if err != nil {
  95. log.Fatalf("Unable to parse client secret file to config: %v", err)
  96. }
  97. sendClient := getClient(ctx, sendConfig, "send.json")
  98. sendService, err := gmail.New(sendClient)
  99. if err != nil {
  100. log.Fatalf("Unable to retrieve gmail Client %v", err)
  101. }
  102. if err := SendEmail(ctx, sendService, "jane1988@gmail.com"); err != nil {
  103. log.Fatalf("failed to send email: %v", err)
  104. }
  105. }
  106. func SendEmail(ctx context.Context, svc *gmail.Service, email string) error {
  107. header := make(map[string]string)
  108. header["To"] = email
  109. header["Subject"] = "hello there"
  110. header["MIME-Version"] = "1.0"
  111. header["Content-Type"] = `text/html; charset="utf-8"`
  112. header["Content-Transfer-Encoding"] = "base64"
  113. var msg string
  114. for k, v := range header {
  115. msg += fmt.Sprintf("%s: %s\n", k, v)
  116. }
  117. msg += "\n" + "Hello, Gmail!"
  118. gmsg := gmail.Message{
  119. Raw: encodeWeb64String([]byte(msg)),
  120. }
  121. _, err := svc.Users.Messages.Send("me", &gmsg).Do()
  122. return err
  123. }
  124. func encodeWeb64String(b []byte) string {
  125. s := base64.URLEncoding.EncodeToString(b)
  126. var i = len(s) - 1
  127. for s[i] == '=' {
  128. i--
  129. }
  130. return s[0 : i + 1]
  131. }

This works perfectly, but without attachments. How can I attach files to the mail?

答案1

得分: 1

也许你可以尝试将头部的Content-Type更改为multipart/mixedRFC 2046,第5.1.3节)或multipart/alternativeRFC 2046,第5.1.4节),并查看如何使用Content-Disposition: attachment; filename=<your file here.ext>

英文:

Maybe you can try change the header Content-Type to multipart/mixed (RFC 2046, Section 5.1.3) or multipart/alternative (RFC 2046, Section 5.1.4) and check how to use Content-Disposition: attachment; filename=&lt;your file here.ext&gt;.

huangapple
  • 本文由 发表于 2016年4月7日 20:05:55
  • 转载请务必保留本文链接:https://go.coder-hub.com/36475677.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定