全部產品
Search
文件中心

Direct Mail:如何通過 SMTP 方式發送帶附件的郵件?

更新時間:Jul 13, 2024

本文介紹如何通過SMTP方式發送帶附件的郵件。

通過 SMTP 的方式發送帶附件的郵件的方法就是:構建一封 MIME 格式的郵件內容。

MIME 基礎知識

說明

附件郵件總大小不超過15MB,一次最多不超過100個附件。

15MB是指smtp發信郵件實際總大小,由於base64編碼郵件代碼會膨脹1.5倍以上,總大小非客戶側看到的大小,附件限制建議按照8MB來準備。若需要發送大附件,建議內容裡加超連結的方式發送。

程式碼範例(python 2.7)

# -*- coding:utf-8 -*-

import urllib, urllib2
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from email.header import Header

# 寄件者地址,通過控制台建立的寄件者地址
username = 'xxx@xxx.com'
# 寄件者密碼,通過控制台建立的寄件者密碼
password = 'XXXXXXXX'

# 收件者地址清單,支援多個收件者,最多30個
rcptlist = ['to1@to.com', 'to2@to.com']
receivers = ','.join(rcptlist)

# 構建 multipart 的郵件訊息
msg = MIMEMultipart('mixed')
msg['Subject'] = 'Test Email'
msg['From'] = username
msg['To'] = receivers

# 構建 multipart/alternative 的 text/plain 部分
alternative = MIMEMultipart('alternative')
textplain = MIMEText('純文字部分', _subtype='plain', _charset='UTF-8')
alternative.attach(textplain)

# 構建 multipart/alternative 的 text/html 部分
texthtml = MIMEText('超文本部分', _subtype='html', _charset='UTF-8')
alternative.attach(texthtml)

# 將 alternative 加入 mixed 的內部
msg.attach(alternative)

# 附件類型
# xlsx 類型的附件
xlsxpart = MIMEApplication(open('測試檔案1.xlsx', 'rb').read())
xlsxpart.add_header('Content-Disposition', 'attachment', filename=Header("測試檔案1.xlsx","utf-8").encode())
msg.attach(xlsxpart)

# jpg 類型的附件
jpgpart = MIMEApplication(open('2.jpg', 'rb').read())
jpgpart.add_header('Content-Disposition', 'attachment', filename=Header("2.jpg","utf-8").encode())
msg.attach(jpgpart)

# mp3 類型的附件
mp3part = MIMEApplication(open('3.mp3', 'rb').read())
mp3part.add_header('Content-Disposition', 'attachment', filename=Header("3.mp3","utf-8").encode())
msg.attach(mp3part)

# 發送郵件
try:
    client = smtplib.SMTP()
    #python 2.7以上版本,若需要使用SSL,可以這樣建立client
    #client = smtplib.SMTP_SSL()

    client.connect('smtpdm.aliyun.com')
    client.login(username, password)
    #寄件者和認證地址必須一致
    client.sendmail(username, rcptlist, msg.as_string())
    client.quit()
    print '郵件發送成功!'
except smtplib.SMTPRecipientsRefused:
    print '郵件發送失敗,收件者被拒絕'
except smtplib.SMTPAuthenticationError:
    print '郵件發送失敗,認證錯誤'
except smtplib.SMTPSenderRefused:
    print '郵件發送失敗,寄件者被拒絕'
except smtplib.SMTPException,e:
    print '郵件發送失敗, ', e.message

程式碼範例(GO)

package main

import (
    "bytes"
    "encoding/base64"
    "fmt"
    "io/ioutil"
    "log"
    "mime"
    "net/smtp"
    "strings"
    "time"
)

// define email interface, and implemented auth and send method
type Mail interface {
    Auth()
    Send(message Message) error
}

type SendMail struct {
    user     string
    password string
    host     string
    port     string
    auth     smtp.Auth
}

type Attachment struct {
    name        string
    contentType string
    withFile    bool
}

type Message struct {
    from        string
    to          []string
    cc          []string
    bcc         []string
    subject     string
    body        string
    contentType string
    attachment  Attachment
}

func main() {
    user := "XXX@XXXXX.top"
    password := "TESXXXXXX"
    host := "smtpdm.aliyun.com"
    port := "80"

    var mail Mail
    mail = &SendMail{user: user, password: password, host: host, port: port}
    message := Message{from: user,
        to:          []string{"XXXXX@qq.com", "XX@qq.com", "XXX@163.com"},
        cc:          []string{},
        bcc:         []string{},
        subject:     "HELLO WORLD",
        body:        "測試內容",
        contentType: "text/plain;charset=utf-8",
        // attachment: Attachment{
        //     name:        "test.jpg",
        //     contentType: "image/jpg",
        //     withFile:    true,
        // },

        attachment: Attachment{
            name:        "D:\\goProjects\\src\\測試pdf.pdf",
            contentType: "application/octet-stream",
            withFile:    true,
        },
    }
    err := mail.Send(message)
    if err != nil {
        fmt.Println("Send mail error!")
        fmt.Println(err)
    } else {
        fmt.Println("Send mail success!")
    }
}

func (mail *SendMail) Auth() {
    // mail.auth = smtp.PlainAuth("", mail.user, mail.password, mail.host)
    mail.auth = LoginAuth(mail.user, mail.password)
}

func (mail SendMail) Send(message Message) error {
    mail.Auth()
    buffer := bytes.NewBuffer(nil)
    boundary := "GoBoundary"
    Header := make(map[string]string)
    Header["From"] = message.from
    Header["To"] = strings.Join(message.to, ";")
    Header["Cc"] = strings.Join(message.cc, ";")
    Header["Bcc"] = strings.Join(message.bcc, ";")
    Header["Subject"] = message.subject
    Header["Content-Type"] = "multipart/mixed;boundary=" + boundary
    Header["Mime-Version"] = "1.0"
    Header["Date"] = time.Now().String()
    mail.writeHeader(buffer, Header)

    body := "\r\n--" + boundary + "\r\n"
    body += "Content-Type:" + message.contentType + "\r\n"
    body += "\r\n" + message.body + "\r\n"
    buffer.WriteString(body)

    if message.attachment.withFile {
        attachment := "\r\n--" + boundary + "\r\n"
        attachment += "Content-Transfer-Encoding:base64\r\n"
        attachment += "Content-Disposition:attachment\r\n"
        attachment += "Content-Type:" + message.attachment.contentType + ";name=\"" + mime.BEncoding.Encode("UTF-8", message.attachment.name) + "\"\r\n"
        buffer.WriteString(attachment)
        defer func() {
            if err := recover(); err != nil {
                log.Fatalln(err)
            }
        }()
        mail.writeFile(buffer, message.attachment.name)
    }

    to_address := MergeSlice(message.to, message.cc)
    to_address = MergeSlice(to_address, message.bcc)

    buffer.WriteString("\r\n--" + boundary + "--")
    err := smtp.SendMail(mail.host+":"+mail.port, mail.auth, message.from, to_address, buffer.Bytes())
    return err
}

func MergeSlice(s1 []string, s2 []string) []string {
    slice := make([]string, len(s1)+len(s2))
    copy(slice, s1)
    copy(slice[len(s1):], s2)
    return slice
}

func (mail SendMail) writeHeader(buffer *bytes.Buffer, Header map[string]string) string {
    header := ""
    for key, value := range Header {
        header += key + ":" + value + "\r\n"
    }
    header += "\r\n"
    buffer.WriteString(header)
    return header
}

// read and write the file to buffer
func (mail SendMail) 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")
        }
    }
}

type loginAuth struct {
    username, password string
}

func LoginAuth(username, password string) smtp.Auth {
    return &loginAuth{username, password}
}

func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
    // return "LOGIN", []byte{}, nil
    return "LOGIN", []byte(a.username), nil
}

func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
    if more {
        switch string(fromServer) {
        case "Username:":
            return []byte(a.username), nil
        case "Password:":
            return []byte(a.password), nil
        }
    }
    return nil, nil
}

其他範例程式碼參考:

SMTP 之 Java 調用樣本

SMTP 之 Python3.6 及以上調用樣本

SMTP 之 CSharp 調用樣本

SMTP 之 PHP 調用樣本

SMTP 之 Nodejs 調用樣本