文档中心>邮件推送>SMTP 文档>发送带附件的邮件

发送带附件的邮件

最近更新时间:2026-01-16 09:18:52

我的收藏
通过 SMTP 的方式发送带附件的邮件的方法,即构建一封 MIME 格式的邮件内容。

邮件 MIME 格式

了解更多协议相关,请参见 MIME 协议
说明:
MIME 消息由消息头和消息体两大部分组成。分别称为 邮件头邮件体

邮件头

说明:
每条信息称为一个域,由域名后加“: ”和信息内容构成,可以是一行,较长的也可以占用多行。
域的首行必须“顶头”写,即左边不能有空白字符(空格和制表符)。
续行则必须以空白字符打头,且一个空白字符不是信息本身固有的(解码时要过滤掉)。
邮件头中不允许出现空行。有一些邮件不能被邮件客户端软件识别,显示的是原始码,就是因为首行是空行。
例如:
内容
示例
Date
Mon, 29 Jun 2009 18:39:03 +0800
From
abc@123.com
To
abc1@123.com
BCC
abc3@123.com
Subject
test
Message-ID
123@123.com
Mime-Version
1.0
域名
含义
Bcc
暗送地址
Cc
抄送地址
Content-Transfer-Encoding
内容的传输编码方式
Content-Type
内容的类型
Date
日期和时间
Delivered-To
发送地址
From
发件人地址
Message-ID
消息 ID
MIME-Version
MIME 版本
Received
传输路径
Reply-To
回复地址
Return-Path
回复地址
Subject
主题
To
收件人地址

邮件体

域名
含义
Content-ID
段体的 ID
Content-Transfer-Encoding
段体的传输编码方式
Content-Location
段体的位置(路径)
Content-Base
段体的基位置
Content-Disposition
段体的安排方式
Content-Type
段体的类型
有些域除了值之外,还带有参数。值与参数、参数与参数之间以“;”分隔。参数名与参数值之间以“=”分隔。
邮件体包含邮件的内容,它的类型由邮件头的Content-Type域指出。
说明:
常见的简单类型有:
text/plain(纯文本)
text/html(超文本)
multipart 类型,是 MIME 邮件的精髓。邮件体被分为多个段,每个段又包含段头和段体两部分,这两部分之间也以空行分隔。
常见的 multipart 类型有三种:
multipart/mixed
multipart/related
multipart/alternative 可从上述名称,得知这些类型各自的含义和用处。它们之间的层次关系可归纳为下图所示:

如果在邮件中要添加附件,必须定义 multipart/mixed 段;如果存在内嵌资源,至少要定义 multipart/related 段;如果纯文本与超文本共存,至少要定义 multipart/alternative 段。
说明:
附件个数不超过10个,单个附件大小不超过4M,总附件大小不超过8M,具体可参见 数据结构

Go 代码示例

package main
import (
"bytes"
"crypto/tls"
"encoding/base64"
"fmt"
"io/ioutil"
"log"
"mime"
"net"
"net/smtp"
"time"
)

// Test465Attachment for port 465
func Test465Attachment() error {
boundary := "GoBoundary"
host := "smtp.qcloudmail.com"
port := 465
email := "abc@cd.com"
password := "***"
toEmail := "test@test123.com"
header := make(map[string]string)
header["From"] = "test " + "<" + email + ">"
header["To"] = toEmail
header["Subject"] = "Test465Attachment"
header["Content-Type"] = "multipart/mixed;boundary=" + boundary
//该字段暂时没有用到 ,默认传1.0
header["Mime-Version"] = "1.0"
//该字段暂时没有用到
header["Date"] = time.Now().String()
bodyHtml := "<!DOCTYPE html>\\n<html>\\n<head>\\n<meta charset=\\"utf-8\\">\\n<title>hello world</title>\\n</head>\\n<body>\\n " +
"<h1>我的第一个标题</h1>\\n <p>我的第一个段落。</p>\\n</body>\\n</html>"
message := ""
for k, v := range header {
message += fmt.Sprintf("%s: %s\\r\\n", k, v)
}
buffer := bytes.NewBuffer(nil)
buffer.WriteString(message)
contentType := "Content-Type: text/html" + "; charset=UTF-8"
body := "\\r\\n--" + boundary + "\\r\\n"
body += contentType + "\\r\\n"
body += "Content-Transfer-Encoding: base64\\r\\n"
body += "\\r\\n" + base64.StdEncoding.EncodeToString([]byte(bodyHtml)) + "\\r\\n"
buffer.WriteString(body)

attachment := "\\r\\n--" + boundary + "\\r\\n"
attachment += "Content-Transfer-Encoding:base64\\r\\n"
attachment += "Content-Disposition:attachment\\r\\n"
attachment += "Content-Type:" + "application/octet-stream" + ";name=\\"" + mime.BEncoding.Encode("UTF-8",
"./go.mod") + "\\"\\r\\n"
buffer.WriteString(attachment)
writeFile(buffer, "./go.mod")
//多个附件往后拼接,最多不能超过10个附件,单个附件不能超过5M,所有附件累计不能超过8-9M,消息体过大会返回EOF
attachment1 := "\\r\\n--" + boundary + "\\r\\n"
attachment1 += "Content-Transfer-Encoding:base64\\r\\n"
attachment1 += "Content-Disposition:attachment\\r\\n"
attachment1 += "Content-Type:" + "application/octet-stream" + ";name=\\"" + mime.BEncoding.Encode("UTF-8",
"./bbbb.txt") + "\\"\\r\\n"
buffer.WriteString(attachment1)
writeFile(buffer, "./bbbb.txt")
defer func() {
if err := recover(); err != nil {
log.Fatalln(err)
}
}()

buffer.WriteString("\\r\\n--" + boundary + "--")
message += "\\r\\n" + body
auth := smtp.PlainAuth(
"",
email,
password,
host,
)
err := SendMailWithTLS(
fmt.Sprintf("%s:%d", host, port),
auth,
email,
[]string{toEmail},
buffer.Bytes(),
)
if err != nil {
fmt.Println("Send email error:", err)
} else {
fmt.Println("Send mail success!")
}
return err
}

// Dial return a smtp client
func Dial(addr string) (*smtp.Client, error) {
conn, err := tls.Dial("tcp", addr, nil)
if err != nil {
log.Println("tls.Dial Error:", err)
return nil, err
}

host, _, _ := net.SplitHostPort(addr)
return smtp.NewClient(conn, host)
}

// SendMailWithTLS send email with tls
func SendMailWithTLS(addr string, auth smtp.Auth, from string,
to []string, msg []byte) (err error) {
//create smtp client
c, err := Dial(addr)
if err != nil {
log.Println("Create smtp client error:", err)
return err
}
defer c.Close()
if auth != nil {
if ok, _ := c.Extension("AUTH"); ok {
if err = c.Auth(auth); err != nil {
log.Println("Error during AUTH", err)
return err
}
}
}
if err = c.Mail(from); err != nil {
return err
}
for _, addr := range to {
if err = c.Rcpt(addr); err != nil {
return err
}
}
w, err := c.Data()
if err != nil {
return err
}
_, err = w.Write(msg)
if err != nil {
return err
}
err = w.Close()
if err != nil {
return err
}
return c.Quit()
}

// writeFile read file to buffer
func writeFile(buffer *bytes.Buffer, fileName string) {
file, err := ioutil.ReadFile(fileName)
if err != nil {
panic(err.Error())
}
payload := make([]byte, base64.StdEncoding.EncodedLen(len(file)))
base64.StdEncoding.Encode(payload, file)
buffer.WriteString("\\r\\n")
for index, line := 0, len(payload); index < line; index++ {
buffer.WriteByte(payload[index])
if (index+1)%76 == 0 {
buffer.WriteString("\\r\\n")
}
}
}

func main() {
Test465Attachment()
}


Python 代码示例

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import smtplib
import os
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
from email.utils import formatdate
from email.header import Header
import ssl

def send_email_with_attachments():
# 邮件服务器配置
host = "smtp.qcloudmail.com" #不同站点请选择不同接入域名
port = 465
email = "abc@cd.com" #控制台注册的smtp用户名
password = "****" #控制台注册的smtp密码
to_email = "test@test123.com"
# 创建MIME消息
msg = MIMEMultipart('mixed')
msg['From'] = f"test <{email}>"
msg['To'] = to_email
msg['Subject'] = "Test465Attachment"
msg['Date'] = formatdate(localtime=True)
msg['Mime-Version'] = "1.0"
# HTML正文内容
html_body = """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"><title>hello world</title>
</head>
<body>
<h1>我的第一个标题</h1>
<p>我的第一个段落。</p>
</body>
</html>
"""
# 添加HTML正文部分
html_part = MIMEText(html_body, 'html', 'utf-8')
msg.attach(html_part)
# 添加附件
attachments = ["./中文名称.txt"]
for attachment_path in attachments:
if os.path.exists(attachment_path):
try:
# 读取附件文件
with open(attachment_path, 'rb') as file:
attachment_data = file.read()
# 创建MIMEBase对象
attachment_part = MIMEBase('application', 'octet-stream')
attachment_part.set_payload(attachment_data)
# 编码为base64
encoders.encode_base64(attachment_part)
# 设置附件头信息
filename = os.path.basename(attachment_path)
encoded_filename = Header(filename, 'utf-8').encode()
attachment_part.add_header(
'Content-Disposition',
f'attachment; filename="{encoded_filename}"'
)
msg.attach(attachment_part)
print(f"成功添加附件: {filename}")
except Exception as e:
print(f"添加附件 {attachment_path} 时出错: {e}")
else:
print(f"附件文件不存在: {attachment_path}")
try:
# 创建SSL上下文
context = ssl.create_default_context()
# 连接SMTP服务器并发送邮件
with smtplib.SMTP_SSL(host, port, context=context) as server:
server.login(email, password)
server.send_message(msg)
print("邮件发送成功!")
return True
except Exception as e:
print(f"发送邮件时出错: {e}")
return False


if __name__ == "__main__":
print("开始发送带附件的邮件...")
send_email_with_attachments()

注意:
如果在终端执行这段编码出现编码类报错,一般是终端的编码不是 UTF-8 导致。