使用 go-imap 很轻松就可以完成一个邮件客户端 最近有收取邮件分析的需求,于是就使用 golang 中的 go-imap 包进行邮件处理,将代码和过程分享出来。 首先在 GO MODULE 申明依赖
1 2 3 4 5 6 7 8 9 module Test go 1.15 require ( github.com/emersion/go -imap v1.2 .0 github.com/emersion/go -imap-id v0.0 .0 -20190926060100 -f94a56b9ecde github.com/emersion/go -sasl v0.0 .0 -20211008083017 -0 b9dcfb154ac github.com/emersion/go -message v0.15 .0 )
接着新建一个 mail.go 里面来写如何处理邮件 连接邮件服务器 我这里用的是网易邮箱
1 2 3 4 5 6 7 c, err := client.DialTLS("imap.163.com:993" , nil ) if err != nil { log.Fatal(err) } log.Println("连接成功" ) defer c.Logout()
登录邮件服务器 接着是登录邮件服务器,这里有个地方要注意,基于 RFC 2971 协议 - IMAP4 ID extension 需要 申明自己身份 否则可能会被拒绝登录,收到错误信息,例如网易邮箱会提示 “NO SELECT Unsafe Login”。协议主要约定了客 户端需要定义一个 ID 字段用以表明身份方便统计分析和定位问题,字段 内的字段名不超过 30 个 8 位字节,值不超过 1024 个 8 位字节, 主要字段有以下几个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 name Name of the program version Version number of the program os Name of the operating system os-version Version of the operating system vendor Vendor of the client/server support-url URL to contact for support address Postal address of contact/vendor date Date program was released, specified as a date-time in IMAP4rev1 command Command used to start the program arguments Arguments supplied on the command line, if any if any environment Description of environment, i.e., UNIX environment variables or Windows registry settings
而 go-imap 作者 emersion 也有支持 IMAP4 ID extension 的包 go-imap-id 已经导入了 简单使用就行
1 2 3 4 idClient := id.NewClient(c) idClient.ID( id.ID{id.FieldName: "IMAPClient" , id.FieldVersion: "1.2.0" }, )
选择邮箱文件夹 不多赘述了看代码就行
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 if err := c.Login("XX@163.com" , "pwd" ); err != nil {log.Fatal(err) } log.Println("登陆成功" ) mailboxes := make (chan *imap.MailboxInfo, 10 ) done := make (chan error , 1 ) go func () { done <- c.List("" , "*" , mailboxes) }() log.Println("邮箱文件夹:" ) for m := range mailboxes { log.Println("* " + m.Name) } if err := <-done; err != nil { log.Fatal(err) } mbox, err := c.Select("INBOX" , false ) if err != nil { log.Fatal(err) }
处理邮件正文 处理邮件正文,包里已经封装处理好了,包括多字节字符的处理,只需要调用就行了。这里需要用到 emersion/go-message 包 设置 imap.CharsetReader 以支持除了 UTF-8 和 ASCII 以外的字符编码,如果不设置则支持 UTF-8 和 ASCII ,像 gb2312、gb18030 这些是无法处理的。
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 imap.CharsetReader = charset.Reader from := uint32 (1 ) to := mbox.Messages if mbox.Messages > 10 { from = mbox.Messages - 10 } seqset := new (imap.SeqSet) seqset.AddRange(from, to) messages := make (chan *imap.Message, 10 ) section := imap.BodySectionName{} items := []imap.FetchItem{section.FetchItem()} done = make (chan error , 1 ) go func () { done <- c.Fetch(seqset, items, messages) }() log.Println("最后十封邮件:" ) imap.CharsetReader = charset.Reader for msg := range messages { r := msg.GetBody(§ion) if r == nil { log.Fatal("服务器未返回邮件正文" ) } mr, err := mail.CreateReader(r) if err != nil { log.Fatal(err) } header := mr.Header var subject string if date, err := header.Date(); err == nil { log.Println("Date:" , date) } if from, err := header.AddressList("From" ); err == nil { log.Println("From:" , from) } if to, err := header.AddressList("To" ); err == nil { log.Println("To:" , to) } if subject, err = header.Subject(); err == nil { log.Println("Subject:" , subject) } for { p, err := mr.NextPart() if err == io.EOF { break } else if err != nil { log.Fatal("NextPart:err " ,err) } switch h := p.Header.(type ) { case *mail.InlineHeader: b, _ := ioutil.ReadAll(p.Body) mailFile := fmt.Sprintf("INBOX/%s.eml" ,subject) f, _ := os.OpenFile(mailFile, os.O_RDWR|os.O_CREATE, 0766 ) f.Write(b) f.Close() case *mail.AttachmentHeader: filename, _ := h.Filename() log.Printf("attachment: %v\n" , filename) } }
其中
1 2 section := imap.BodySectionName{} items := []imap.FetchItem{section.FetchItem()}
这里获取的是全部邮件内容,如果只想获取信封头的话可以使用 imap.FetchEnvelope
相关GITHUB地址:go-imap
我的知乎文章地址:知乎