依據 smtp協議的簡單golang 的發郵件實現
協議格式如下
From:sender_user@demo.net
To:to_user@demo.net
Subject:這是主題
Mime-Version:1.0 //通常是1.0
Content-Type:Multipart/mixed;Boundary="THIS_IS_BOUNDARY_JUST_MAKE_YOURS" //boundary為分界字符,跟http傳文件時類似
Date:當前時間
--THIS_IS_BOUNDARY_JUST_MAKE_YOURS //boundary前邊需要加上連接符 -- , 首部和第一個boundary之間有兩個空行
Content-Type:text/plain;chart-set=utf-8
//單個部分的首部和正文間有個空行
這是正文1
這是正文2
--THIS_IS_BOUNDARY_JUST_MAKE_YOURS //每個部分的與上一部分之間一個空行
Content-Type:image/jpg;name="test.jpg"
Content-Transfer-Encoding:base64
Content-Description:這個是描述
//單個部分的首部和正文間有個空行
base64編碼的文件 //文件內容使用base64 編碼,單行不超過80字節,需要插入\r\n進行換行
--THIS_IS_BOUNDARY_JUST_MAKE_YOURS-- //最后結束的標識--boundary--
golang 代碼實現如下
//email/email.go
package email
import (
"bytes"
"encoding/base64"
"fmt"
"io/ioutil"
"net/smtp"
"strings"
"time"
)
//SendEmailWithAttachment : send email with attachment
func SendEmailWithAttachment(user, passwd, host, to, subject string) error {
hp := strings.Split(host, ":")
auth := smtp.PlainAuth("", user, passwd, hp[0])
buffer := bytes.NewBuffer(nil)
boudary := "THIS_IS_BOUNDARY_JUST_MAKE_YOURS"
header := fmt.Sprintf("To:%s\r\n"+
"From:%s\r\n"+
"Subject:%s\r\n"+
"Content-Type:multipart/mixed;Boundary=\"%s\"\r\n"+
"Mime-Version:1.0\r\n"+
"Date:%s\r\n", to, user, subject, boudary, time.Now().String())
buffer.WriteString(header)
fmt.Print(header)
msg1 := "\r\n\r\n--" + boudary + "\r\n" + "Content-Type:text/plain;charset=utf-8\r\n\r\n這是正文啊\r\n"
buffer.WriteString(msg1)
fmt.Print(msg1)
msg2 := fmt.Sprintf(
"\r\n--%s\r\n"+
"Content-Transfer-Encoding: base64\r\n"+
"Content-Disposition: attachment;\r\n"+
"Content-Type:image/jpg;name=\"test.jpg\"\r\n", boudary)
buffer.WriteString(msg2)
fmt.Print(msg2)
attachmentBytes, err := ioutil.ReadFile("./test.jpg")
if err != nil {
fmt.Println("ReadFile ./test.jpg Error : " + err.Error())
return err
}
b := make([]byte, base64.StdEncoding.EncodedLen(len(attachmentBytes)))
base64.StdEncoding.Encode(b, attachmentBytes)
buffer.WriteString("\r\n")
fmt.Print("\r\n")
fmt.Print("圖片base64編碼")
for i, l := 0, len(b); i < l; i++ {
buffer.WriteByte(b[i])
if (i+1)%76 == 0 {
buffer.WriteString("\r\n")
}
}
buffer.WriteString("\r\n--" + boudary + "--")
fmt.Print("\r\n--" + boudary + "--")
sendto := strings.Split(to, ";")
err = smtp.SendMail(host, auth, user, sendto, buffer.Bytes())
return err
}
//email_test.go
package email
import "testing"
func TestSendEmailWithAttachment(t *testing.T) {
err := SendEmailWithAttachment("xx@example.com", "passwd", "smtp.xx.com:25", "xx@example.com", "測試附件")
if err != nil {
t.Fatal(err)
}
}
go test 打印輸出如下
To:xx@example.com
From:xx@example.com
Subject:測試附件
Content-Type:multipart/mixed;Boundary="THIS_IS_BOUNDARY_JUST_MAKE_YOURS"
Mime-Version:1.0
Date:2016-09-11 12:17:37.268146477 +0800 CST
--THIS_IS_BOUNDARY_JUST_MAKE_YOURS
Content-Type:text/plain;charset=utf-8
這是正文啊
--THIS_IS_BOUNDARY_JUST_MAKE_YOURS
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
Content-Type:image/jpg;name="test.jpg"
圖片base64編碼
--THIS_IS_BOUNDARY_JUST_MAKE_YOURS--
部分實現參考 https://github.com/scorredoira/email 具體項目中可使用該庫。
遇到的問題
- 對boundary格式理解錯誤,頭部寫的boundary 在下邊用的時候要拼上--前綴
- 含有附件的,第一個boundary 和 header 中間有兩個空行
- 結束標記為 --boundary--
- 循環文件內容進行插入換行時,如果出現邏輯錯誤,則傳輸的附件顯示異常,無法正常查看和下載。