一、普通邮件的发送

在golang.org官方页面上,net/smtp模块给出了我们如何直接进行邮件发送和简单的带密码验证的邮件的发送。不过由于不进行tls ssl 安全认证的smtp协议,很容易在网络传输中被抓包获取用户名密码,所以目前各大主流邮箱(QQ、163、gmail)等都不再做为主流(163还在支持),在开始tls加密邮件传输之前,先看下官网的示例(这里用的github上golang项目页的地址):

 1package main
 2import (
 3    "log"
 4    "net/smtp"
 5)
 6func main() {
 7    // Set up authentication information.
 8    auth := smtp.PlainAuth("", "[email protected]", "password", "mail.example.com")
 9    // Connect to the server, authenticate, set the sender and recipient,
10    // and send the email all in one step.
11    to := []string{"[email protected]"}
12    msg := []byte("To: [email protected]\r\n" +
13        "Subject: discount Gophers!\r\n" +
14        "\r\n" +
15        "This is the email body.\r\n")
16    err := smtp.SendMail("mail.example.com:25", auth, "[email protected]", to, msg)
17    if err != nil {
18        log.Fatal(err)
19    }
20}

二、使用tls加密传输的

使用tls传输的,使用的端口是465,不再是默认的25,其需要使用到crypto/tls模块,具体代码如下:

 1package main
 2import (
 3    "crypto/tls"
 4    "fmt"
 5    "log"
 6    "net"
 7    "net/smtp"
 8)
 9func main() {
10    host := "smtp.163.com"
11    port := 465
12    email := "[email protected]"
13    password := "password"
14    toEmail := "[email protected]"
15    header := make(map[string]string)
16    header["From"] = "test" + ""
17    header["To"] = toEmail
18    header["Subject"] = "邮件标题"
19    header["Content-Type"] = "text/html; charset=UTF-8"
20    body := "我是一封电子邮件!golang发出. by:www.361way.com "
21    message := ""
22    for k, v := range header {
23        message += fmt.Sprintf("%s: %s\r\n", k, v)
24    }
25    message += "\r\n" + body
26    auth := smtp.PlainAuth(
27        "",
28        email,
29        password,
30        host,
31    )
32    err := SendMailUsingTLS(
33        fmt.Sprintf("%s:%d", host, port),
34        auth,
35        email,
36        []string{toEmail},
37        []byte(message),
38    )
39    if err != nil {
40        panic(err)
41    }
42}
43//return a smtp client
44func Dial(addr string) (*smtp.Client, error) {
45    conn, err := tls.Dial("tcp", addr, nil)
46    if err != nil {
47        log.Println("Dialing Error:", err)
48        return nil, err
49    }
50    //分解主机端口字符串
51    host, _, _ := net.SplitHostPort(addr)
52    return smtp.NewClient(conn, host)
53}
54//参考net/smtp的func SendMail()
55//使用net.Dial连接tls(ssl)端口时,smtp.NewClient()会卡住且不提示err
56//len(to)>1时,to[1]开始提示是密送
57func SendMailUsingTLS(addr string, auth smtp.Auth, from string,
58    to []string, msg []byte) (err error) {
59    //create smtp client
60    c, err := Dial(addr)
61    if err != nil {
62        log.Println("Create smpt client error:", err)
63        return err
64    }
65    defer c.Close()
66    if auth != nil {
67        if ok, _ := c.Extension("AUTH"); ok {
68            if err = c.Auth(auth); err != nil {
69                log.Println("Error during AUTH", err)
70                return err
71            }
72        }
73    }
74    if err = c.Mail(from); err != nil {
75        return err
76    }
77    for _, addr := range to {
78        if err = c.Rcpt(addr); err != nil {
79            return err
80        }
81    }
82    w, err := c.Data()
83    if err != nil {
84        return err
85    }
86    _, err = w.Write(msg)
87    if err != nil {
88        return err
89    }
90    err = w.Close()
91    if err != nil {
92        return err
93    }
94    return c.Quit()
95}

调用后,邮件发送示例如下:

golang-email
golang-email

由于上面header里,from里的名称是test,所以上图中可以看出,发件中显示的就是test,就里可以根据自己的需要进行修改。

三、发送html格式的邮件

如果发送的格式,想要比较丰富,就需要使用html格式的body进行发送,具体示例如下:

 1package main
 2import (
 3    "fmt"
 4    "net/smtp"
 5    "strings"
 6)
 7func SendToMail(user, password, host, to, subject, body, mailtype string) error {
 8    hp := strings.Split(host, ":")
 9    auth := smtp.PlainAuth("", user, password, hp[0])
10    var content_type string
11    if mailtype == "html" {
12        content_type = "Content-Type: text/" + mailtype + "; charset=UTF-8"
13    } else {
14        content_type = "Content-Type: text/plain" + "; charset=UTF-8"
15    }
16    msg := []byte("To: " + to + "\r\nFrom: " + user + ">\r\nSubject: " + "\r\n" + content_type + "\r\n\r\n" + body)
17    send_to := strings.Split(to, ";")
18    err := smtp.SendMail(host, auth, user, send_to, msg)
19    return err
20}
21func main() {
22    user := "yang**@yun*.com"
23    password := "***"
24    host := "smtp.exmail.qq.com:25"
25    to := "[email protected]"
26    subject := "使用Golang发送邮件"
27    body := `
28        <html>
29        <body>
30        <h3>
31        "Test send to email ,from www.361way.com"
32        </h3>
33        </body>
34        </html>
35        `
36    fmt.Println("send email")
37    err := SendToMail(user, password, host, to, subject, body, "html")
38    if err != nil {
39        fmt.Println("Send mail error!")
40        fmt.Println(err)
41    } else {
42        fmt.Println("Send mail success!")
43    }
44}

上面的send_to := strings.Split(to, “;”)是针对多个收件人时做的处理,而msg := []byte 是强制做了格式转换。

四、发送可自定义模板的邮件

一些网站需要通过自动邮件发送时,可以有多个模板可以选择,这里就可以使用”html/template”模块发送格式邮件,该html模板可以自定义修改,要一些变量引用的地方,以类似于jinja2的形式进行引用即可,go代码如下:

 1package main
 2import (
 3    "bytes"
 4    "fmt"
 5    "html/template"
 6    "net/smtp"
 7)
 8var auth smtp.Auth
 9func main() {
10    auth = smtp.PlainAuth("", "[email protected]", "password", "smtp.gmail.com")
11    templateData := struct {
12        Name string
13        URL  string
14    }{
15        Name: "myblog",
16        URL:  "https://blog.361way.com",
17    }
18    r := NewRequest([]string{"[email protected]"}, "Hello Junk!", "Hello, World!")
19    err := r.ParseTemplate("template.html", templateData)
20    if err := r.ParseTemplate("template.html", templateData); err == nil {
21        ok, _ := r.SendEmail()
22        fmt.Println(ok)
23    }
24}
25//Request struct
26type Request struct {
27    from    string
28    to      []string
29    subject string
30    body    string
31}
32func NewRequest(to []string, subject, body string) *Request {
33    return &Request{
34        to:      to,
35        subject: subject,
36        body:    body,
37    }
38}
39func (r *Request) SendEmail() (bool, error) {
40    mime := "MIME-version: 1.0;\nContent-Type: text/html; charset=\"UTF-8\";\n\n"
41    subject := "Subject: " + r.subject + "!\n"
42    msg := []byte(subject + mime + "\n" + r.body)
43    addr := "smtp.gmail.com:587"
44    if err := smtp.SendMail(addr, auth, "[email protected]", r.to, msg); err != nil {
45        return false, err
46    }
47    return true, nil
48}
49func (r *Request) ParseTemplate(templateFileName string, data interface{}) error {
50    t, err := template.ParseFiles(templateFileName)
51    if err != nil {
52        return err
53    }
54    buf := new(bytes.Buffer)
55    if err = t.Execute(buf, data); err != nil {
56        return err
57    }
58    r.body = buf.String()
59    return nil
60}

对应的模块文件内容如下(template.html):

<pre data-language="HTML">```markup

<html>
</head>
<body>
<p>
    Hello {{.Name}}
    <a href="{{.URL}}">Confirm you web address</a>
</p>
</body>
</html>

该模块文件使用的就是结构体templateData里的数据。

</body></html>