GO操作邮件服务器 IMAP协议
本文章是记录在本地连接邮件服务器 测试收发是否正常过程中遇到的问题
GO操作邮件服务器 IMAP协议
教案工程代码发送邮件与测试邮件 此处是演示正确流程
教案工程代码发送邮件时使用的账户需要跟游戏中的账户进行分离开
- 固定使用的发送方进行以下修改
1
2
3
4
5
6
7
8
// email/app.go
const (
host = "mail. - "
port = 587
user = "t1@ - "
pwd = "csT29587"
sender = "tank@ - "
)
- 每次需要发送邮件,接收邮箱地址修改为:
1
2
3
4
const (
user = "t2@ - "
pwd = "csT29587"
)
进行测试时,可以检测此时发送的邮件内容(From,To,Body等等)
使用下面的包使用
imap协议去访问老金邮件服务器上的邮件使用理由:
- github上有
300Fork跟2.1kStar - 现在还在维护
1 2 3
"github.com/emersion/go-imap" "github.com/emersion/go-imap/client" "github.com/emersion/go-message/mail"
- github上有
代码流程
创建邮件的客户端并使用接收方的邮件账户进行登录。这里的登录必须是接收方的账号登录!
账号为”t2@ - “
密码为” - “
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
const ( //这里由于使用imap协议,用到SSL的话,需要连接到服务器的993端口 mailserver = "mail. - :993" user = "t2@ - " pwd = " - " ) tlsConfig := &tls.Config{ ServerName: "mail. - ", } c, err := client.DialTLS(mailserver, tlsConfig) if err != nil { t.Errorf("初始化邮箱客户端失败!:%v", err) return } defer c.LoggedOut() t.Log("成功连接邮箱服务器") if err := c.Login(user, pwd); err != nil { t.Errorf("登录邮箱失败!:%v", err) return }
在成功登录之后,就使用这个客户端实体,获取到接收信箱中的最新邮件。由于此时发送与接受都是一个服务器上,所以延迟几乎可以忽略
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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79
//这里的接收信箱,是服务器上作了缓存,会将邮件保存在服务器文件夹中。同理也会有其余的信箱文件夹,例如已发送信件的文件夹 //这里的名称不能被修改 mailbox, err := c.Select("Inbox", false) if err != nil { t.Errorf("获取发件箱失败!:%v", err) return } // 确保文件夹中有邮件 if mailbox.Messages == 0 { t.Fatal("文件夹为空,没有邮件可供读取。") } //创建序号对象 seqset := new(imap.SeqSet) seqset.AddNum(mailbox.Messages) // 最新邮件的序号 // 获取邮件 section := &imap.BodySectionName{} messages := make(chan *imap.Message, 1) done := make(chan error, 1) //开一个协程去获取最新邮件实体 go func() { done <- c.Fetch(seqset, []imap.FetchItem{section.FetchItem()}, messages) }() msg := <-messages if msg == nil { t.Log("No message retrieved") return } // 解析邮件内容 mailr := msg.GetBody(section) if mailr == nil { t.Log("Server didn't return message body") return } mr, err := mail.CreateReader(mailr) if err != nil { t.Log(err) return } // 输出邮件基本信息,这里就可以进行检测。 subject, _ := mr.Header.Subject() fmt.Printf("Subject: %s\n", subject) from, _ := mr.Header.AddressList("From") for _, addr := range from { fmt.Printf("From: %s <%s>\n", addr.Name, addr.Address) } to, _ := mr.Header.AddressList("To") for _, addr := range to { fmt.Printf("To: %s <%s>\n", addr.Name, addr.Address) } // 解析邮件正文并提取验证码 // var code string var bodyContent string for { p, err := mr.NextPart() if err == io.EOF { break } if err != nil { t.Errorf("读取正文出错:%v", err) continue } //此处能正确获取到这个Body,也可以进行内容的获取 switch p.Header.(type) { case *mail.InlineHeader: bodyBytes, _ := io.ReadAll(p.Body) bodyContent = string(bodyBytes) fmt.Println("Body:", bodyContent) // 输出邮件正文 } }
源可用代码
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 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
package auth import ( "crypto/tls" "fmt" "io" "regexp" "tank/common/log" "tank/common/pg_conn" "tank/user_mgt/internal/db" "tank/user_mgt/internal/maintain" "tank/user_mgt/pkg/utils" "testing" "github.com/emersion/go-imap" "github.com/emersion/go-imap/client" "github.com/emersion/go-message/mail" ) const ( mailserver = "mail. - :993" user = "t2@ - " pwd = " - " ) func TestGetEmail(){ tlsConfig := &tls.Config{ ServerName: "mail. - ", } c, err := client.DialTLS(mailserver, tlsConfig) if err != nil { t.Errorf("初始化邮箱客户端失败!:%v", err) return } defer c.LoggedOut() t.Log("成功连接邮箱服务器") if err := c.Login(user, pwd); err != nil { t.Errorf("登录邮箱失败!:%v", err) return } mailbox, err := c.Select("Inbox", false) if err != nil { t.Errorf("获取发件箱失败!:%v", err) return } // 确保文件夹中有邮件 if mailbox.Messages == 0 { t.Fatal("文件夹为空,没有邮件可供读取。") } // 获取最新 10 封邮件的 UID 范围 seqset := new(imap.SeqSet) // 确保 seqset 范围在有效范围内 seqset.AddNum(mailbox.Messages) // 最新邮件的序号 // 获取邮件 section := &imap.BodySectionName{} messages := make(chan *imap.Message, 1) done := make(chan error, 1) go func() { done <- c.Fetch(seqset, []imap.FetchItem{section.FetchItem()}, messages) }() msg := <-messages if msg == nil { t.Log("No message retrieved") return } // 解析邮件内容 mailr := msg.GetBody(section) if mailr == nil { t.Log("Server didn't return message body") return } mr, err := mail.CreateReader(mailr) if err != nil { t.Log(err) return } // 输出邮件基本信息 subject, _ := mr.Header.Subject() fmt.Printf("Subject: %s\n", subject) from, _ := mr.Header.AddressList("From") for _, addr := range from { fmt.Printf("From: %s <%s>\n", addr.Name, addr.Address) } to, _ := mr.Header.AddressList("To") for _, addr := range to { fmt.Printf("To: %s <%s>\n", addr.Name, addr.Address) } var bodyContent string for { p, err := mr.NextPart() if err == io.EOF { break } if err != nil { t.Errorf("读取正文出错:%v", err) continue } //此处能正确获取到这个 switch p.Header.(type) { case *mail.InlineHeader: bodyBytes, _ := io.ReadAll(p.Body) bodyContent = string(bodyBytes) fmt.Println("Body:", bodyContent) // 输出邮件正文 } } }
返回信息(我的测试返回)
设计思路 要测试邮件服务是否正常
- 使用golang创建客户端1、2 调用函数使用客户端1进行发送 再使用客户端2获取邮件服务器中成功接收的邮件查看是否能接受到 由于同一个邮件服务器,因此延迟几乎为0 就能马上读到
- 随后就是对emersion/go-imap包的使用方式 由于涉及到发与收 因此需要一直阻塞等待邮件的到来 使用for select去等待 并且有一个无缓冲区的channal去等待信息的填充,也能实现阻塞等待,让整个协程等待
本文由作者按照 CC BY 4.0 进行授权
