Golang发送邮件

1.入门案例

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package main

import (
    "crypto/tls"
    "fmt"
    "gopkg.in/gomail.v2"
    "time"
)

func main() {
    m := gomail.NewMessage()
    m.SetHeader("From", "sender@example.com")
    m.SetHeader("To", "recipient1@example.com", "recipient2@example.com")
    m.SetHeader("Cc", "cc1@example.com", "cc2@example.com")
    m.SetHeader("Subject", "Hello!")
    m.SetBody("text/html", fmt.Sprintf("This is a test email, from go language program, current time is %s", time.Now().Format("2006-01-02 15:04:05")))
    m.Attach("./gomail/test.png")  // 添加附件

    // 设置邮件服务器信息(以163邮箱为例)
    d := gomail.NewDialer("smtp.163.com", 465, "sender@example.com", "XXXXXXXXXXXXXXXX") // 这个需要要对应的邮件去申请SMTP授权密码

    // 跳过TLS证书验证,设置InsecureSkipVerify为true(生产环境不建议这样使用)
    // d.TLSConfig = &tls.Config{InsecureSkipVerify: true}

    if err := d.DialAndSend(m); err != nil {
       panic(err)
    }
    fmt.Println("Mail sent successfully!")
}

注意,163 邮箱的 SMTP 服务器地址是 smtp.163.com,端口为 465,并且可以选择是否启用TLS证书验证(设置 TLSConfig)。

这个需要根据邮箱的类型进行更改。

2.读取配置文件

通过ini文件,配置发送方邮箱,密码、smtp服务器、smtp端口,接收方、抄送方邮箱等信息。

ini文件示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
; 发送邮件的配置信息

[sender]
email = sender@example.com
password = XXXXXXXXXXXXXXXX
smtp_server = smtp.163.com
smtp_port = 465

[recipient]
to_list = recipient1@example.com,recipient2@example.com
cc_list = cc2@example.com,cc2@example.com

接收方和抄送方可以有多个,用逗号分隔即可。

这里使用ini库读取ini配置文件

代码示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package main

import (
    "crypto/tls"
    "fmt"
    "gopkg.in/gomail.v2"
    "gopkg.in/ini.v1"
    "log"
    "strings"
    "time"
)

func main() {

    // 初始化邮件发送配置
    cfg, err := ini.Load("./gomail/email_config.ini")
    if err != nil {
       log.Fatalf("Failed to load email config: %v", err)
    }

    // 读取发送邮箱信息
    senderEmail := cfg.Section("sender").Key("email").String()
    password := cfg.Section("sender").Key("password").String()
    smtpServer := cfg.Section("sender").Key("smtp_server").String()
    smtpPort := cfg.Section("sender").Key("smtp_port").MustInt(587)

    // 连接到 SMTP 服务器
    d := gomail.NewDialer(smtpServer, smtpPort, senderEmail, password)
    d.TLSConfig = &tls.Config{InsecureSkipVerify: true}

    // 读取接收方和抄送方邮箱信息
    toListStr := cfg.Section("recipient").Key("to_list").String()
    toLists := strings.Split(toListStr, ",")
    ccListStr := cfg.Section("recipient").Key("cc_list").String()
   if len(ccLists[0]) > 0 {
		m.SetHeader("Cc", ccLists...)
	}

    // 设置发送方、接收方、抄送方邮箱
    m := gomail.NewMessage()
    m.SetHeader("From", senderEmail)
    m.SetHeader("To", toLists...)
    m.SetHeader("Cc", ccLists...)

    // 设置邮件内容
    m.SetHeader("Subject", "Hello!")
    m.SetBody("text/html", fmt.Sprintf("This is a test email, from go language program, current time is %s", time.Now().Format("2006-01-02 15:04:05")))
    m.Attach("./gomail/test.png")

    // 发送邮件
    if err := d.DialAndSend(m); err != nil {
       log.Fatalf("Failed to send email, err: %v", err)
    }
    log.Println("Email sent successfully!")
}

这段代码演示了如何使用 Go 语言的 gomail 包发送邮件,其中邮件配置信息从一个 INI 文件中加载。

以下是代码的解释:

  1. 导入必要的包:代码中使用了多个包,包括 crypto/tlsgopkg.in/gomail.v2gopkg.in/ini.v1 等,用于处理加密通信、发送邮件以及读取配置文件。

  2. 从 INI 配置文件加载邮件发送配置:代码中使用 ini.Load 函数加载配置文件 ./gomail/email_config.ini,该配置文件包含了发送邮件所需的信息,如发送方邮箱、SMTP 服务器信息、接收方邮箱等。

  3. 读取配置信息:通过使用 ini 包读取 INI 文件的各个部分,获取发送邮箱、SMTP 服务器、接收方邮箱等的信息。

  4. 连接到 SMTP 服务器:使用 gomail.NewDialer 创建一个用于与 SMTP 服务器通信的 Dialer 对象,并设置 SMTP 服务器地址、端口、发送邮箱地址以及密码。此处还设置了 InsecureSkipVerifytrue,以允许跳过 TLS 证书验证。这是一个不安全的设置,通常用于测试或开发环境。

  5. 读取接收方和抄送方邮箱信息:通过读取配置文件中的接收方邮箱列表和抄送方邮箱列表,并使用逗号分隔字符串,将它们转换为字符串切片。

  6. 创建邮件消息:使用 gomail.NewMessage 创建一个邮件消息对象,并设置发送方、接收方、抄送方、主题、正文内容以及可能的附件等信息。

  7. 发送邮件:使用 d.DialAndSend(m) 方法将邮件消息 m 发送给 SMTP 服务器。如果发送过程中出现错误,将通过 log.Fatalf 记录错误信息。

  8. 最后,如果邮件成功发送,代码将输出一条成功的消息。

3.邮件发送验证码

2023.11.05

1.初始化

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func initInfo() (dialer *gomail.Dialer, senderEmail string, toLists []string, ccLists []string) {
    // 初始化邮件发送配置
    cfg, err := ini.Load("./send_email/email_config.ini")
    if err != nil {
       log.Fatalf("Failed to load email config: %v", err)
    }

    // 读取发送邮箱信息
    senderEmail = cfg.Section("sender").Key("email").String()
    password := cfg.Section("sender").Key("password").String()
    smtpServer := cfg.Section("sender").Key("smtp_server").String()
    smtpPort := cfg.Section("sender").Key("smtp_port").MustInt(587)

    // 连接到 SMTP 服务器
    dialer = gomail.NewDialer(smtpServer, smtpPort, senderEmail, password)

    // 读取接收方和抄送方邮箱信息
    toListStr := cfg.Section("recipient").Key("to_list").String()
    toLists = strings.Split(toListStr, ",")
    ccListStr := cfg.Section("recipient").Key("cc_list").String()
    ccLists = strings.Split(ccListStr, ",")
    return dialer, senderEmail, toLists, ccLists
}

读取配置文件中的信息,初始化dialer,并返回。同时将配置文件中的senderEmail,toLists,ccLists信息一并返回,供后面的步骤使用。

2.html模板文件

示例如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!doctype html>
<html>
<head>
<meta charset='UTF-8'>
<style>
    .content {text-indent:20px;}
</style>
</head>
<body style="font-size: 18px;">
<p><strong>尊敬的 Ecodego 用户,您好:</strong></p>
<div class="content">
<p>您的帐号 <strong>xxxxx</strong> 正在进行注册操作,请确认为本人操作。以下是您的验证码,请在10分钟内完成验证:</p>
<p style="font-size: 22px;"><strong>{{.VerificationCode}}</strong></p>
<p>(如非本人操作,请忽略此邮件)</p>
</div>
<p>感谢您的使用!</p>
<p><a href='https://www.ecodego.com/'>Ecodego</a></p>
<p>{{.CurrentTime}}</p>
<hr />
<p style="font-size: 15px;">此为系统邮件,请勿回复。</p>
</body>
</html>

上面的的模板中,设置了两个模板参数{{.VerificationCode}}{{.CurrentTime}},它们是动态变化的,需要后端传入。

用户名 xxxxx 也应该是动态变化的,这里为了方便,做了简化处理。

3.对模板进行渲染

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
func renderHtmlContent(verificationCode string) string {
    // 读取 HTML 模板文件
    templateBytes, err := os.ReadFile("./send_email/注册模板.html")
    if err != nil {
       panic(err)
    }

    // 创建 HTML 模板
    tmpl, err := template.New("emailTemplate").Parse(string(templateBytes))
    if err != nil {
       panic(err)
    }

    // 创建缓冲区来存储模板渲染后的内容
    var renderedEmailContent string
    buffer := &bytes.Buffer{}

    // 将验证码插入到模板
    data := struct {
       VerificationCode string
       CurrentTime      string
    }{
       VerificationCode: verificationCode,
       CurrentTime:      time.Now().Format("2006-01-02 15:04:05"),
    }

    // 渲染模板
    err = tmpl.Execute(buffer, data)
    if err != nil {
       panic(err)
    }

    // 获取渲染后的 HTML 内容
    renderedEmailContent = buffer.String()
    return renderedEmailContent
}

4.随机数验证码

1
2
3
4
5
6
7
func generateRandomCode() string {
    r := rand.New(rand.NewSource(time.Now().UnixNano())) // 指定随机种子
    minVal := 100000                                     // 最小的六位数
    maxVal := 999999                                     // 最大的六位数
    code := minVal + r.Intn(maxVal-minVal+1)
    return fmt.Sprintf("%06d", code) // 将随机数格式化为六位数的字符串
}

生成随机数验证码,将返回结果传入模板渲染函数renderHtmlContent()中。

5.添加邮件内容

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
func addEmailContent(senderEmail string, toLists []string, ccLists []string, emailContent string) *gomail.Message {
    // 添加邮件内容
    mail := gomail.NewMessage()
    mail.SetHeader("From", senderEmail)
    mail.SetHeader("To", toLists...)
    if len(ccLists[0]) > 0 {
       mail.SetHeader("Cc", ccLists...)
    }

    mail.SetHeader("Subject", "【Ecodego】邮箱验证码")
    mail.SetBody("text/html", emailContent)
    return mail
}

使用gomail包新生成一个邮件信息对象mail,设置mailHeaderFromToCcSubject等)、Body信息(将前面renderHtmlContent()函数得到的渲染之后的html文件内容传入到mailBody中),然后返回mail对象。

这里使用len(ccLists[0]) > 0来判断ccLists是否为空而不是len(ccLists)>0,原因:

前面读取ini配置文件时:

1
2
ccListStr := cfg.Section("recipient").Key("cc_list").String()
ccLists = strings.Split(ccListStr, ",")

如果recipient.cc_list标签没写,或者改标签值为空时,得到的ccLists为包含一个空字符的切片,其长度len=1,如果recipient.cc_list标签设置了一个对象(即无逗号分隔),则ccLists的长度也为1,所以我们不能通过判断len(ccLists)==1来判断recipient.cc_list标签是否为空。

故我们需要取出ccLists的第0个元素,判断其长度是否为0(等于0则说明recipient.cc_list标签为空,否则不为空)

6.main函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
func main() {
    dialer, senderEmail, toLists, ccLists := initInfo() // 初始化dialer,并从配置文件中获取senderEmail,toLists,ccLists

    verificationCode := generateRandomCode()            // 生成随机验证码
    emailContent := renderHtmlContent(verificationCode) // 渲染html内容

    mail := addEmailContent(senderEmail, toLists, ccLists, emailContent) // 添加邮件内容

    if err := dialer.DialAndSend(mail); err != nil { // 发送邮件
       log.Fatalf("Failed to send email, err: %v", err)
    }
    log.Println("Email sent successfully!")
}

main函数中调用前面的几个函数(包括初始化dialer、读取配置文件、生成随机验证码、渲染html内容,添加邮件内容),并通过dialer发送邮件内容mail

0%