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

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

Send email with attachments in golang

问题

这是代码:

package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/user"
	"path/filepath"

	"golang.org/x/net/context"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/gmail/v1"
	"encoding/base64"
	"io/ioutil"
)

// getClient 使用上下文和配置来检索令牌,然后生成客户端。它返回生成的客户端。
func getClient(ctx context.Context, config *oauth2.Config, configFileName string) *http.Client {
	cacheFile, err := tokenCacheFile(configFileName)
	if err != nil {
		log.Fatalf("无法获取缓存凭据文件的路径。%v", err)
	}
	tok, err := tokenFromFile(cacheFile)
	if err != nil {
		tok = getTokenFromWeb(config)
		saveToken(cacheFile, tok)
	}
	return config.Client(ctx, tok)
}

// getTokenFromWeb 使用配置来请求令牌。它返回检索到的令牌。
func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
	authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
	fmt.Printf("在浏览器中打开以下链接,然后输入授权码:\n%v\n", authURL)

	var code string
	if _, err := fmt.Scan(&code); err != nil {
		log.Fatalf("无法读取授权码 %v", err)
	}

	tok, err := config.Exchange(oauth2.NoContext, code)
	if err != nil {
		log.Fatalf("无法从网络中检索令牌 %v", err)
	}
	return tok
}

// tokenCacheFile 生成凭据文件的路径/文件名。它返回生成的凭据路径/文件名。
func tokenCacheFile(filename string) (string, error) {
	usr, err := user.Current()
	if err != nil {
		return "", err
	}
	tokenCacheDir := filepath.Join(usr.HomeDir, ".credentials")
	os.MkdirAll(tokenCacheDir, 0700)
	return filepath.Join(tokenCacheDir,
		url.QueryEscape(filename)), err
}

// tokenFromFile 从给定的文件路径检索令牌。它返回检索到的令牌和遇到的任何读取错误。
func tokenFromFile(file string) (*oauth2.Token, error) {
	f, err := os.Open(file)
	if err != nil {
		return nil, err
	}
	t := &oauth2.Token{}
	err = json.NewDecoder(f).Decode(t)
	defer f.Close()
	return t, err
}

// saveToken 使用文件路径创建文件并将令牌存储在其中。
func saveToken(file string, token *oauth2.Token) {
	fmt.Printf("将凭据文件保存到:%s\n", file)
	f, err := os.Create(file)
	if err != nil {
		log.Fatalf("无法缓存OAuth令牌:%v", err)
	}
	defer f.Close()
	json.NewEncoder(f).Encode(token)
}

func main() {
	ctx := context.Background()

	b, err := ioutil.ReadFile("client_secret.json")
	if err != nil {
		log.Fatalf("无法读取客户端密钥文件:%v", err)
	}

	sendConfig, err := google.ConfigFromJSON(b, gmail.GmailSendScope)
	if err != nil {
		log.Fatalf("无法解析客户端密钥文件为配置:%v", err)
	}
	sendClient := getClient(ctx, sendConfig, "send.json")

	sendService, err := gmail.New(sendClient)
	if err != nil {
		log.Fatalf("无法检索Gmail客户端:%v", err)
	}

	if err := SendEmail(ctx, sendService, "jane1988@gmail.com"); err != nil {
		log.Fatalf("发送邮件失败:%v", err)
	}
}

func SendEmail(ctx context.Context, svc *gmail.Service, email string) error {

	header := make(map[string]string)
	header["To"] = email
	header["Subject"] = "你好"
	header["MIME-Version"] = "1.0"
	header["Content-Type"] = `text/html; charset="utf-8"`
	header["Content-Transfer-Encoding"] = "base64"

	var msg string
	for k, v := range header {
		msg += fmt.Sprintf("%s: %s\n", k, v)
	}
	msg += "\n" + "你好,Gmail!"

	gmsg := gmail.Message{
		Raw: encodeWeb64String([]byte(msg)),
	}

	_, err := svc.Users.Messages.Send("me", &gmsg).Do()
	return err
}

func encodeWeb64String(b []byte) string {
	s := base64.URLEncoding.EncodeToString(b)

	var i = len(s) - 1
	for s[i] == '=' {
		i--
	}

	return s[0 : i+1]
}

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

英文:

Here is the code:

package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"net/url"
"os"
"os/user"
"path/filepath"
"golang.org/x/net/context"
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
"google.golang.org/api/gmail/v1"
"encoding/base64"
"io/ioutil"
)
// getClient uses a Context and Config to retrieve a Token
// then generate a Client. It returns the generated Client.
func getClient(ctx context.Context, config *oauth2.Config, configFileName string) *http.Client {
cacheFile, err := tokenCacheFile(configFileName)
if err != nil {
log.Fatalf("Unable to get path to cached credential file. %v", err)
}
tok, err := tokenFromFile(cacheFile)
if err != nil {
tok = getTokenFromWeb(config)
saveToken(cacheFile, tok)
}
return config.Client(ctx, tok)
}
// getTokenFromWeb uses Config to request a Token.
// It returns the retrieved Token.
func getTokenFromWeb(config *oauth2.Config) *oauth2.Token {
authURL := config.AuthCodeURL("state-token", oauth2.AccessTypeOffline)
fmt.Printf("Go to the following link in your browser then type the " +
"authorization code: \n%v\n", authURL)
var code string
if _, err := fmt.Scan(&code); err != nil {
log.Fatalf("Unable to read authorization code %v", err)
}
tok, err := config.Exchange(oauth2.NoContext, code)
if err != nil {
log.Fatalf("Unable to retrieve token from web %v", err)
}
return tok
}
// tokenCacheFile generates credential file path/filename.
// It returns the generated credential path/filename.
func tokenCacheFile(filename string) (string, error) {
usr, err := user.Current()
if err != nil {
return "", err
}
tokenCacheDir := filepath.Join(usr.HomeDir, ".credentials")
os.MkdirAll(tokenCacheDir, 0700)
return filepath.Join(tokenCacheDir,
url.QueryEscape(filename)), err
}
// tokenFromFile retrieves a Token from a given file path.
// It returns the retrieved Token and any read error encountered.
func tokenFromFile(file string) (*oauth2.Token, error) {
f, err := os.Open(file)
if err != nil {
return nil, err
}
t := &oauth2.Token{}
err = json.NewDecoder(f).Decode(t)
defer f.Close()
return t, err
}
// saveToken uses a file path to create a file and store the
// token in it.
func saveToken(file string, token *oauth2.Token) {
fmt.Printf("Saving credential file to: %s\n", file)
f, err := os.Create(file)
if err != nil {
log.Fatalf("Unable to cache oauth token: %v", err)
}
defer f.Close()
json.NewEncoder(f).Encode(token)
}
func main() {
// Use oauth2.NoContext if there isn't a good context to pass in.
//ctx := context.TODO()
ctx := context.Background()
b, err := ioutil.ReadFile("client_secret.json")
if err != nil {
log.Fatalf("Unable to read client secret file: %v", err)
}
// If modifying these scopes, delete your previously saved credentials
// at ~/.credentials/gmail-go-quickstart.json
sendConfig, err := google.ConfigFromJSON(b, gmail.GmailSendScope)
if err != nil {
log.Fatalf("Unable to parse client secret file to config: %v", err)
}
sendClient := getClient(ctx, sendConfig, "send.json")
sendService, err := gmail.New(sendClient)
if err != nil {
log.Fatalf("Unable to retrieve gmail Client %v", err)
}
if err := SendEmail(ctx, sendService, "jane1988@gmail.com"); err != nil {
log.Fatalf("failed to send email: %v", err)
}
}
func SendEmail(ctx context.Context, svc *gmail.Service, email string) error {
header := make(map[string]string)
header["To"] = email
header["Subject"] = "hello there"
header["MIME-Version"] = "1.0"
header["Content-Type"] = `text/html; charset="utf-8"`
header["Content-Transfer-Encoding"] = "base64"
var msg string
for k, v := range header {
msg += fmt.Sprintf("%s: %s\n", k, v)
}
msg += "\n" + "Hello, Gmail!"
gmsg := gmail.Message{
Raw: encodeWeb64String([]byte(msg)),
}
_, err := svc.Users.Messages.Send("me", &gmsg).Do()
return err
}
func encodeWeb64String(b []byte) string {
s := base64.URLEncoding.EncodeToString(b)
var i = len(s) - 1
for s[i] == '=' {
i--
}
return s[0 : i + 1]
}

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:

确定