Golang中如何将发送的邮件保存到邮件客户端的"已发送"文件夹
Golang中如何将发送的邮件保存到邮件客户端的"已发送"文件夹 我正在开发一个电子邮件客户端,它分别使用SMTP包(go-mail)和IMAP包(emersion)来发送和接收邮件。IMAP可以从电子邮件服务器读取邮件。但是,SMTP包在发送邮件时不会将邮件保存到“已发送”文件夹中。如何使用我正在使用的包来实现这个功能?或者是否有其他支持此功能的包?我们尝试过使用IMAP的Append选项,它可以将已发送的邮件存储在“已发送”文件夹中,但它没有“Message_Id”和其他通常在读取邮件时可用的头部信息。
我看到使用PHP开发的Roundcube电子邮件客户端中有相同的功能选项。我不知道如何在Golang中实现这一点。
如果有人能提供更好的解决方案,那将非常有帮助。
提前感谢。
更多关于Golang中如何将发送的邮件保存到邮件客户端的"已发送"文件夹的实战教程也可以访问 https://www.itying.com/category-94-b0.html
更多关于Golang中如何将发送的邮件保存到邮件客户端的"已发送"文件夹的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
在Golang中实现将已发送邮件保存到IMAP服务器的"已发送"文件夹,可以通过以下步骤完成:
核心解决方案
使用go-imap包的Append方法将已发送邮件存储到IMAP服务器,并确保包含完整的邮件头部信息。
实现示例
package main
import (
"bytes"
"fmt"
"log"
"time"
"github.com/emersion/go-imap"
"github.com/emersion/go-imap/client"
"github.com/emersion/go-message/mail"
)
// SendAndSaveEmail 发送邮件并保存到已发送文件夹
func SendAndSaveEmail(smtpClient *smtp.Client, imapClient *client.Client,
from, to, subject, body string) error {
// 1. 构建完整的邮件内容(包含头部)
var msgBuffer bytes.Buffer
// 创建邮件头部
h := mail.Header{}
h.Set("From", from)
h.Set("To", to)
h.Set("Subject", subject)
h.Set("Date", time.Now().Format(time.RFC1123Z))
h.Set("Message-ID", generateMessageID(from))
h.Set("MIME-Version", "1.0")
h.Set("Content-Type", "text/plain; charset=utf-8")
// 写入头部
if err := mail.WriteHeader(&msgBuffer, h); err != nil {
return fmt.Errorf("写入邮件头部失败: %v", err)
}
// 写入邮件正文
msgBuffer.WriteString(body)
// 2. 使用SMTP发送邮件
msgBytes := msgBuffer.Bytes()
if err := sendViaSMTP(smtpClient, from, to, msgBytes); err != nil {
return fmt.Errorf("SMTP发送失败: %v", err)
}
// 3. 保存到IMAP的已发送文件夹
if err := saveToSentFolder(imapClient, msgBytes); err != nil {
return fmt.Errorf("保存到已发送文件夹失败: %v", err)
}
return nil
}
// saveToSentFolder 将邮件保存到已发送文件夹
func saveToSentFolder(imapClient *client.Client, msg []byte) error {
// 选择已发送文件夹
sentFolder := "Sent"
if err := imapClient.Select(sentFolder, false); err != nil {
// 如果文件夹不存在,尝试创建
if err := imapClient.Create(sentFolder); err != nil {
return fmt.Errorf("创建已发送文件夹失败: %v", err)
}
if err := imapClient.Select(sentFolder, false); err != nil {
return fmt.Errorf("选择已发送文件夹失败: %v", err)
}
}
// 设置邮件标志
flags := []string{imap.SeenFlag, imap.DraftFlag}
date := time.Now()
// 使用Append保存邮件
if err := imapClient.Append(sentFolder, flags, date, imap.Literal(msg)); err != nil {
return fmt.Errorf("追加邮件到已发送文件夹失败: %v", err)
}
return nil
}
// generateMessageID 生成唯一的Message-ID
func generateMessageID(from string) string {
return fmt.Sprintf("<%d.%s@localhost>", time.Now().UnixNano(), from)
}
// sendViaSMTP 通过SMTP发送邮件
func sendViaSMTP(smtpClient *smtp.Client, from, to string, msg []byte) error {
// 这里使用你的SMTP发送逻辑
// 示例使用net/smtp:
// return smtp.SendMail(server, auth, from, []string{to}, msg)
return nil
}
完整邮件构建示例
package main
import (
"bytes"
"fmt"
"mime"
"time"
"github.com/emersion/go-message"
"github.com/emersion/go-message/mail"
)
// BuildCompleteEmail 构建包含完整头部的邮件
func BuildCompleteEmail(from, to, subject, body string) ([]byte, error) {
var buf bytes.Buffer
// 创建邮件头部
h := mail.Header{}
h.Set("From", from)
h.Set("To", to)
h.Set("Subject", mime.QEncoding.Encode("utf-8", subject))
h.Set("Date", time.Now().Format(time.RFC1123Z))
h.Set("Message-ID", fmt.Sprintf("<%d.%s>", time.Now().UnixNano(), from))
h.Set("MIME-Version", "1.0")
h.Set("Content-Type", "text/plain; charset=utf-8")
h.Set("Content-Transfer-Encoding", "quoted-printable")
// 创建邮件写入器
mw, err := mail.CreateWriter(&buf, h)
if err != nil {
return nil, err
}
defer mw.Close()
// 创建文本部分
tw, err := mw.CreateInline()
if err != nil {
return nil, err
}
th := make(message.Header)
th.Set("Content-Type", "text/plain; charset=utf-8")
th.Set("Content-Transfer-Encoding", "quoted-printable")
pw, err := tw.CreatePart(th)
if err != nil {
return nil, err
}
// 写入正文
if _, err := pw.Write([]byte(body)); err != nil {
return nil, err
}
pw.Close()
return buf.Bytes(), nil
}
使用第三方包简化
考虑使用go-message和go-smtp的组合:
import (
"github.com/emersion/go-message"
"github.com/emersion/go-smtp"
)
// 使用go-smtp发送并自动保存
type Backend struct {
imapClient *client.Client
}
func (b *Backend) Send(from string, to []string, r io.Reader) error {
// 读取完整邮件
msgBytes, err := io.ReadAll(r)
if err != nil {
return err
}
// 保存到已发送文件夹
if err := b.saveToSentFolder(msgBytes); err != nil {
return err
}
// 实际发送逻辑
return nil
}
关键点
- 完整的邮件头部:确保在调用
Append之前,邮件包含Message-ID、Date、From、To等所有必需头部 - 正确的MIME格式:使用
go-message/mail包构建符合标准的MIME邮件 - 时间戳:为
Append方法提供正确的发送时间 - 邮件标志:设置适当的IMAP标志(如
\Seen)
这种方法与Roundcube的实现原理相同,都是通过IMAP的APPEND命令将已发送邮件存储到服务器。

