需要用到 emersion/go-message 包 设置 imap.CharsetReader 以支持除了 UTF-8 和 ASCII 以外的字符编码,如果不设置则支持 UTF-8 和 ASCII ,像 gb2312、gb18030 这些是无法处理的。

下面是转载内容:

 golang 使用 go-imap 一键收发中文邮件

使用 go-imap 很轻松就可以完成一个邮件客户端

最近有收取邮件分析的需求,于是就使用 golang 中的 go-imap 包进行邮件处理,将代码和过程分享出来。

首先在 GO MODULE 申明依赖

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-0b9dcfb154ac // indirect
        github.com/emersion/go-message v0.15.0
)

接着新建一个 mail.go 里面来写如何处理邮件

连接邮件服务器 我这里用的是网易邮箱

    log.Println("连接服务器中...")

	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 位字节,主要字段有以下几个

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 已经导入了 简单使用就行

idClient := id.NewClient(c)
	idClient.ID(
		id.ID{id.FieldName: "IMAPClient", id.FieldVersion: "1.2.0"}, // 随便定义申明自己身份就行 
	)

登录和选择邮箱文件夹 不多赘述了

// 登录
	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 这些是无法处理的。

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(&section)
		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)
			}
		}

其中

section := imap.BodySectionName{}
items := []imap.FetchItem{section.FetchItem()}

这里获取的是全部邮件内容,如果只想获取信封头的话可以使用 imap.FetchEnvelope