请检查
ToBytes()
方法,在我看来问题出在哪里。
package mail
import (
"bytes"
"encoding/base64"
"fmt"
"mime/multipart"
"net/http"
"net/smtp"
"os"
"path/filepath"
"strings"
"github.com/personal-mailer/props"
)
type Sender struct {
auth smtp.Auth
}
type Message struct {
To []string
CC []string
BCC []string
Subject string
Body string
Attachments map[string][]byte
}
func New(smtpCred props.SMTPCredentials) *Sender {
auth := smtp.PlainAuth("", smtpCred.User, smtpCred.Pass, smtpCred.Host)
return &Sender{auth}
}
func (s *Sender) Send(m *Message, smtpCred props.SMTPCredentials) error {
hostAndport := fmt.Sprintf("%s:%d", smtpCred.Host, smtpCred.Port)
auth := smtp.PlainAuth("", smtpCred.User, smtpCred.Pass, smtpCred.Host)
return smtp.SendMail(hostAndport, auth, "[email protected]", m.To, m.ToBytes())
}
func NewMessage(s, b string) *Message {
return &Message{Subject: s, Body: b, Attachments: make(map[string][]byte)}
}
func (m *Message) AttachFile(filePaths []string) error {
for _, path := range filePaths {
b, err := os.ReadFile(path)
if err != nil {
return err
}
_, fileName := filepath.Split(path)
m.Attachments[fileName] = b
}
return nil
}
func (m *Message) ToBytes() []byte {
buf := bytes.NewBuffer(nil)
withAttachments := len(m.Attachments) > 0
buf.WriteString(fmt.Sprintf("From: %s\n", "[email protected]"))
buf.WriteString(fmt.Sprintf("Subject: %s\n", m.Subject))
buf.WriteString(fmt.Sprintf("To: %s\n", strings.Join(m.To, ",")))
if len(m.CC) > 0 {
buf.WriteString(fmt.Sprintf("Cc: %s\n", strings.Join(m.CC, ",")))
}
if len(m.BCC) > 0 {
buf.WriteString(fmt.Sprintf("Bcc: %s\n", strings.Join(m.BCC, ",")))
}
buf.WriteString("MIME-Version: 1.0\n")
writer := multipart.NewWriter(buf)
boundary := writer.Boundary()
if withAttachments {
buf.WriteString(fmt.Sprintf("Content-Type: multipart/mixed; boundary=%s\n\n", boundary))
// Add the email body as a part
buf.WriteString(fmt.Sprintf("--%s\n", boundary))
buf.WriteString("Content-Type: text/plain; charset=utf-8\n")
buf.WriteString(m.Body)
buf.WriteString(fmt.Sprintf("\n--%s\n", boundary))
// Add attachments
for k, v := range m.Attachments {
buf.WriteString(fmt.Sprintf("--%s\n", boundary))
buf.WriteString(fmt.Sprintf("Content-Type: %s\n", http.DetectContentType(v)))
buf.WriteString("Content-Transfer-Encoding: base64\n")
buf.WriteString(fmt.Sprintf("Content-Disposition: attachment; filename=\"%s\"\n\n", k))
b := make([]byte, base64.StdEncoding.EncodedLen(len(v)))
base64.StdEncoding.Encode(b, v)
buf.Write(b)
buf.WriteString(fmt.Sprintf("\n--%s\n", boundary))
buf.WriteString("--\n")
buf.WriteString("--\n")
}
} else {
buf.WriteString("Content-Type: text/plain; charset=utf-8\n\n")
buf.WriteString(m.Body)
}
return buf.Bytes()
}
代码正在工作,就像收件人正在接收带有附件(如果包含)的邮件一样。
我正在尝试使用边界来了解为什么要附加此 att00001.txt 文件,但这不起作用
如果您能实际按照您的意图记录您的代码,以便人们能够理解您的实现是否被破坏或者您对 MIME 的理解是否错误,这将会很有帮助。
很明显,每个附件后面的两个
buf.WriteString("--\n")
都是错误的(不清楚你认为它们应该做什么)并且结束边界(在最后一个 MIME 部分之后)丢失。
有关多部分内容类型的更多信息,请参阅 RFC 2046 第 5.1 节,特别是关于边界作用的 5.1.1 节。 另外,由于您已经使用了 multipart/mime,因此使用 multipart.CreatePart 来处理复杂的 MIME 部分和边界内容而不是自己实现会更有意义。