SMTP(Simple Mail Transfer Protocol)即簡(jiǎn)單郵件傳輸協(xié)議,它是一組用于由源地址到目的地址傳送郵件的規(guī)則,由它來(lái)控制信件的中轉(zhuǎn)方式,絕大多數(shù)語(yǔ)言如C++ Java等都通過(guò)庫(kù)的方式支持了他,當(dāng)然,go語(yǔ)言也不會(huì)例外
很重要
這個(gè)一定要寫在前面,建議自己嘗試的時(shí)候呢,多申請(qǐng)幾個(gè)郵箱.
不要用自己的常用郵箱.
不要用自己的常用郵箱.
不要用自己的常用郵箱.
國(guó)內(nèi)大多數(shù)郵箱都有數(shù)量限制,單日發(fā)的太多容易被封.
smtp庫(kù)
smtp標(biāo)準(zhǔn)庫(kù)就是golang實(shí)現(xiàn)郵件發(fā)送的一個(gè)標(biāo)準(zhǔn)庫(kù),你可以查看src中的net/stmp
來(lái)查看他的源碼
stmp庫(kù)只有兩個(gè)文件,auth.go與smtp.go,前者是用來(lái)驗(yàn)證的,后者是用發(fā)發(fā)送郵件的,auth.go比較簡(jiǎn)單,通常也不會(huì)自己去實(shí)現(xiàn),所以僅做了解即可,用發(fā)是固定的,后面的例子會(huì)演示
我們主要來(lái)看一下smtp.go中的SendMail()方法,下面為函數(shù)簽名
func SendMail(addr string, a Auth, from string, to []string, msg []byte) error {}
參數(shù)含義如下
addr ?郵件服務(wù)器地址,一般與端口一起,如騰訊的郵箱 smtp.exmail.qq.com:25
a ??????通過(guò)Auth.go中的PlainAuth()方法驗(yàn)證發(fā)件人用戶名密碼后獲得
from ?發(fā)件人郵箱
to ?????收件人郵箱列表
msg ??郵件內(nèi)容
利用smtp來(lái)發(fā)送郵件
smtp庫(kù)不需要我們視線接口,我們只需要構(gòu)造好參數(shù),通過(guò)SendMail()
來(lái)發(fā)送郵件即可
我們來(lái)模擬向騰訊郵箱中發(fā)送一封郵件,首先來(lái)構(gòu)造參數(shù),騰訊郵件服務(wù)器地址為smtp.qq.com,端口為25(大部分郵件服務(wù)器的端口都是25),常用郵箱的郵件服務(wù)器地址我會(huì)在最后在備注中貼出
addr := "smtp.qq.com:25"
除此之外,我們還需要構(gòu)造a,from,to,msg四個(gè)參數(shù)
首先 使用auth.go中的PlainAuth()來(lái)生成a,也就是Auth類型,這邊是需要發(fā)件人的郵箱,郵件服務(wù)器,郵箱密碼,我這里演示通過(guò)我的一個(gè)qq郵箱向我的另外一個(gè)qq郵箱發(fā)送郵件
獲取auth時(shí)如果不是TLS連接第一個(gè)參數(shù)可以給空字符串,我們一般郵件都不需要指定第一個(gè)參數(shù),后面參數(shù)就分別為 郵箱 密碼 郵箱所在的郵件服務(wù)器
auth := smtp.PlainAuth("", "你的qq號(hào)碼@qq.com", "password", "smtp.qq.com")
from發(fā)件人也就是上面驗(yàn)證auth用到的郵箱,zhangsheng_1992@sohu.com
from := "你的qq號(hào)碼@qq.com"
to為收件人,是一個(gè)[]string
to := []string{"收件人qq號(hào)碼@qq.com"}
最后是msg
msg := []byte("Hello World!")
整體代碼如下:
package main
import (
"fmt"
"net/smtp"
)
func main() {
addr := "smtp.qq.com:25"
auth := smtp.PlainAuth("", "115354xxxxx@qq.com", "password", "smtp.qq.com")
from := "115354xxxxx@qq.com"
to := []string{"2974xxxxx@qq.com"}
msg := []byte("Hello World!")
err := smtp.SendMail(addr, auth, from, to, msg)
fmt.Println(err)
}
注意: 大部分個(gè)人郵箱都有smtp保護(hù),所以你可能需要登陸郵箱設(shè)置一下
比如qq郵箱你需要設(shè)置一個(gè)授權(quán)碼,保護(hù)你的密碼嘛
點(diǎn)擊設(shè)置,賬戶,開啟pop3/smtp郵件保護(hù),開啟服務(wù)的第一個(gè)選項(xiàng),根據(jù)提示獲取授權(quán)碼,然后在上面程序密碼處填寫授權(quán)碼
而sohu郵箱就簡(jiǎn)單些,勾選開啟服務(wù)即可
其他郵箱的設(shè)置大同小異,請(qǐng)自行設(shè)置,運(yùn)行程序,然后去郵箱查看
構(gòu)造郵件頭
我門發(fā)現(xiàn)上面收到的郵件沒(méi)有標(biāo)題,也沒(méi)有發(fā)件人,更沒(méi)有收件人及內(nèi)容信息,這是為什么呢?
其實(shí)這部分信息在msg中,需要我們來(lái)構(gòu)造,包括
subject 郵件標(biāo)題
send 郵件接收人,與上面的from區(qū)分開,所以我改個(gè)名字
receiver 郵件接收人
type 郵件類型 如html郵件 純文本郵件等等
我門來(lái)構(gòu)造這一部分,注意,這些參數(shù)全部時(shí)字符串,我們使用fmt.Sprintf()方法來(lái)生成這個(gè)string,\r\n表示換行,固定格式
subject := fmt.Sprintf("Subject: %s\r\n", "測(cè)試郵件")
send := fmt.Sprintf("From: %s 測(cè)試發(fā)件郵箱\r\n", "11535xxx.com")
receiver := fmt.Sprintf("To: %s\r\n", "2974xxx@qq.com")
content_type := "Content-Type: text/plain" + "; charset=UTF-8\r\n\r\n"
content := "Hello World!"
msg := []byte(subject + send + receiver + content_type + content)
addr := "smtp.qq.com:25"
auth := smtp.PlainAuth("", "11535xxxxx@qq.com", "password", "smtp.qq.com")
from := "11535xxxxx@qq.com"
to := []string{"2974xxxxx@qq.com"}
err := smtp.SendMail(addr, auth, from, to, msg)
fmt.Println(err)
這次我們來(lái)看接收到的郵件
是不是信息都全了?
關(guān)于郵件頭,需要記住
1.它們都是字符串拼接而成的,順序?yàn)?/strong>
郵件標(biāo)題 \r\n
發(fā)件人 \r\n
收件人 \r\n
郵件類型與字符編碼 \r\n\r\n
郵件內(nèi)容
2.郵件類型與郵件內(nèi)容之間需要2個(gè)換行,即2組\r\n,否則無(wú)法看到郵件內(nèi)容,就如同第一次那樣
3.如果接收到的郵件為亂碼,那么檢查字符編碼
發(fā)送html郵件
發(fā)送html郵件,僅需要設(shè)置content_type,也就是郵件類型中的Content-Type: text/html;即:
content_type = "Content-Type: text/html; charset=UTF-8\r\n\r\n" //和郵件內(nèi)容間兩組換行
一般我門不會(huì)把html當(dāng)做一個(gè)字符串寫到程序中,那樣修改起來(lái)很麻煩,我們通常的做法是將html寫成一個(gè)模版,然后讀區(qū)這個(gè)模版,新建一個(gè)temp.html,內(nèi)容如下:
<html>
<head>
<meta charset="utf-8" />
<title>測(cè)試郵件</title>
</head>
<body>
<p class="title">我是測(cè)試郵件</p>
<p>內(nèi)容1: %s</p>
<p>內(nèi)容2: %s</p>
<img src="%s" alt="圖片"/>
<p><a href="%s">查看詳情</a>
</body>
</html>
然后通過(guò)os.Open()打開并讀取這個(gè)模版
file, _ := os.Open("temp.html")
defer file.Close()
p := make([]byte, 2<<10)//設(shè)置大一點(diǎn) 讓一次性讀進(jìn)去
if _, err := file.Read(p); err != nil {
fmt.Println(err)
os.Exit(0)
}
content := fmt.Sprintf(string(p), "1", "2", "http://img3.doubanio.com/lpic/s24524807.jpg", "4")//去網(wǎng)上隨便找的張圖 測(cè)試而已,如果你收到的郵件圖片打不開請(qǐng)換一個(gè)圖片鏈接(隨便網(wǎng)上找張圖,右鍵獲取圖片地址)
其他部分不變,記得設(shè)置content_type 編碼,再次發(fā)送
golang的吉祥物就被我們通過(guò)郵件發(fā)出去了,當(dāng)然你還可以增加一些css和js來(lái)豐富一封html郵件的,但這不是本文重點(diǎn)
完整代碼
package main
import (
"fmt"
"net/smtp"
"os"
)
func main() {
file, _ := os.Open("temp.html")
defer file.Close()
p := make([]byte, 2<<10)
if _, err := file.Read(p); err != nil {
fmt.Println(err)
os.Exit(0)
}
content := fmt.Sprintf(string(p), "1", "2", "http://img3.doubanio.com/lpic/s24524807.jpg", "4")
subject := fmt.Sprintf("Subject: %s\r\n", "測(cè)試郵件")
send := fmt.Sprintf("From: %s 測(cè)試發(fā)件郵箱\r\n", "11535xxxxx.com")
receiver := fmt.Sprintf("To: %s\r\n", "2974xxxxx@qq.com")
content_type := "Content-Type: text/html" + "; charset=UTF-8\r\n\r\n"
msg := []byte(subject + send + receiver + content_type + content)
addr := "smtp.qq.com:25"
auth := smtp.PlainAuth("", "11535xxxxx@qq.com", "paaword", "smtp.qq.com")
from := "11535xxxxx@qq.com"
to := []string{"2974xxxxx@qq.com"}
err := smtp.SendMail(addr, auth, from, to, msg)
fmt.Println(err)
}
發(fā)送附件
一個(gè)標(biāo)準(zhǔn)的郵件包含 郵件頭,郵件內(nèi)容,附件,下面,我就來(lái)介紹如何發(fā)送一個(gè)帶附件的郵件
與發(fā)送html相同,發(fā)送一個(gè)帶附件的郵件,也需要指定Content-Type參數(shù),并指定一個(gè)分隔符,將郵件頭,正文部分與附件分隔開,分隔符可以任意設(shè)置
content_type := "Content-Type: multipart/mixed; boundary=分隔符\r\n"
分隔符以 --分隔符
開頭,以--分隔符--
結(jié)尾,然后,為了兼容性,需要將附件編碼,為了增加理解,我在代碼中增加了過(guò)多注釋,可能影響閱讀體驗(yàn),建議拷貝下來(lái)自己邊運(yùn)行邊理解,一定要注意換行!!!!
package main
import (
"encoding/base64"
"fmt"
// "io/ioutil"
"net/smtp"
"os"
)
func main() {
//mailcontent用來(lái)保存郵件的有所內(nèi)容
var mailcontent string
//設(shè)置內(nèi)容間的分隔符 盡量不要用漢字,除此之外設(shè)置成什么都可以
delimiter := "module"
//設(shè)置頭信息頭部分
subject := fmt.Sprintf("Subject: %s\r\n", "測(cè)試郵件")
send := fmt.Sprintf("From: %s 測(cè)試發(fā)件郵箱\r\n", "11535xxxxx@qq.com")
receiver := fmt.Sprintf("To: %s\r\n", "2974xxxxx@qq.com") + "MIME-Version: 1.0\r\n"
content_type := "Content-Type: multipart/mixed; boundary=" + delimiter + "\r\n"
//郵件描述 這個(gè)可以不設(shè)置也沒(méi)關(guān)系
content_type += "Content-Description: 我有附件\r\n"
header := subject + send + receiver + content_type
//郵件頭部分設(shè)置完畢
//分割頭信息與普通文本郵件 重要!!!!!!!
mailcontent = header + fmt.Sprintf("\r\n--%s\r\n", delimiter)
//你可以同時(shí)發(fā)送html 但需要注意分隔 你可以把這段注釋掉只發(fā)送html郵件
htmlcontent := "Content-Type: text/html; charset=UTF-8\r\n\r\n"
htmlcontent += `<html><head></head><body><h1 style="color:red">我是html內(nèi)容</h1></body></html>`
mailcontent += htmlcontent
//附件部分
filepath := "/Users/zhangsheng/go/a.png"
filename := "a.png"
//分隔 html內(nèi)容與附件內(nèi)容
fileheader := fmt.Sprintf("\r\n--%s\r\n", delimiter)
fileheader += "Content-Type: application/octet-stream\r\n"
fileheader += "Content-Description: 附一個(gè)Go文件\r\n"
fileheader += "Content-Transfer-Encoding: base64\r\n"
fileheader += "Content-Disposition: attachment; filename=\"" + filename + "\"\r\n\r\n"
//讀取附件內(nèi)容并編碼
file, _ := os.Open(filepath)
fileinfo := make([]byte, 2<<16)//這邊需要設(shè)置一個(gè)足夠大的容量來(lái)保證可以一次性讀取,后面學(xué)習(xí)ioutil和bufio庫(kù)的時(shí)候會(huì)學(xué)習(xí)到更簡(jiǎn)便的辦法
if n, err := file.Read(fileinfo); err != nil {
fmt.Println(err)
} else {
fileinfo = fileinfo[:n]
}
filecontent := base64.StdEncoding.EncodeToString(fileinfo)
// 我們可以使用ioutil庫(kù)來(lái)實(shí)現(xiàn)讀取 這樣就不用像os Read()設(shè)置一個(gè)足夠大的[]byte
// fileinfo, _ := ioutil.ReadFile(filepath)
// filecontent := make([]byte, base64.StdEncoding.EncodedLen(len(attaData)))
// base64.StdEncoding.Encode(filecontent, attaData)
filemessage := fileheader + filecontent
//分隔結(jié)束
mailcontent += filemessage + "\r\n--" + delimiter + "--\r\n\r\n"
msg := []byte(mailcontent)
fmt.Println(string(msg))
addr := "smtp.qq.com:25"
auth := smtp.PlainAuth("", "11535xxxxx@qq.com", "password", "smtp.qq.com")
from := "11535xxxxx@qq.com"
to := []string{"297xxxxx@qq.com"}
err := smtp.SendMail(addr, auth, from, to, msg)
fmt.Println(err)
}
輸出的內(nèi)容如下:
Subject: 測(cè)試郵件
From: 11535xxxxx@qq.com 測(cè)試發(fā)件郵箱
To: 2974xxxxx@qq.com
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=module ?看這里!我設(shè)置的分隔符叫module
Content-Description: 這是一封帶附檔的郵件
--module 第一個(gè)分隔符將郵件人信息與郵件內(nèi)容分隔開
Content-Type: text/html; charset=UTF-8
<html><head></head><body><h1 style="color:red">我是html內(nèi)容</h1></body></html>
--module 第二個(gè)分隔符 將郵件內(nèi)容與附件分隔開
Content-Type: application/octet-stream
Content-Description: 附一個(gè)Png文件
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="a.png"
//這里是很長(zhǎng)的一串編碼后的結(jié)果
--module-- 分隔符結(jié)束
如果你要發(fā)送多個(gè)附件,簡(jiǎn)單,繼續(xù)用分隔符分開第一個(gè)附件與第二個(gè)區(qū)分開即可
然后取接收郵件吧
郵件群發(fā)
群發(fā)郵件也非常簡(jiǎn)單,無(wú)論是簡(jiǎn)單文件還是html文件
首先在receiver里用;
隔開收件人,如:
receiver := fmt.Sprintf("To: %s\r\n", "2974xxxxx@qq.com;b.qq.com;c.qq.com")
然后在to切片中也增加收件人就可以啦
to := []string{"2974xxxxx@qq.com","b.qq.com","c.qq.com"}
總結(jié)
本章我門講解了smtp庫(kù)的m,他實(shí)現(xiàn)了郵件發(fā)送的規(guī)則并且可以用來(lái)發(fā)送郵件,我們講解了普通郵件,html郵件和附件,以及組合類型郵件,由于郵件和html并不是講解重點(diǎn),所以很多東西并沒(méi)有講的很細(xì),比如Content-Type,html只是 分隔符 還有包括內(nèi)嵌郵件等其他使用上的東西
但掌握了smtp庫(kù)的用法以后,你僅需要取了解這部分內(nèi)容,并按要求構(gòu)造你的郵件字符串就可以
附件--常用郵箱地址
注:pop3為收件箱地址,一些客戶端郵件管理程序會(huì)使用這個(gè)
IMAP是另外一種郵件協(xié)議 我門常用的都是SMTP協(xié)議
阿里云郵箱(mail.aliyun.com):
POP3服務(wù)器地址:pop3.aliyun.com(SSL加密端口:995洲守;非加密端口:110)
SMTP服務(wù)器地址:smtp.aliyun.com(SSL加密端口:465梗醇;非加密端口:25)
IMAP服務(wù)器地址:imap.aliyun.com(SSL加密端口:993撒蟀;非加密端口:143)
谷歌郵箱(google.com):
POP3服務(wù)器地址:pop.gmail.com(SSL啟用端口:995)
SMTP服務(wù)器地址:smtp.gmail.com(SSL啟用端口:587)
新浪郵箱(sina.com):
POP3服務(wù)器地址:pop3.sina.com.cn(端口:110)
SMTP服務(wù)器地址:smtp.sina.com.cn(端口:25)
Tom郵箱(top.com):
POP3服務(wù)器地址:pop.tom.com(端口:110)
SMTP服務(wù)器地址:smtp.tom.com(端口:25)
網(wǎng)易郵箱(163.com):
POP3服務(wù)器地址:pop.163.com(端口:110)
SMTP服務(wù)器地址:smtp.163.com(端口:25)
126郵箱:
POP3服務(wù)器地址:pop.live.com(端口:995)
SMTP服務(wù)器地址:smtp.126.com(端口:25)
雅虎郵箱(yahoo.com):
POP3服務(wù)器地址:pop.mail.yahoo.com
SMTP服務(wù)器地址:smtp.mail.yahoo.com
雅虎中國(guó)(yahoo.com.cn):
POP3服務(wù)器地址:pop.mail.yahoo.com.cn(端口:995)
SMTP服務(wù)器地址:smtp.mail.yahoo.com.cn(端口:587)
雅虎郵箱POP3的SSL不啟用端口為110保屯,POP3的SSL啟用端口995;SMTP的SSL不啟用端口為25竟终,SMTP的SSL啟用端口為465。
Foxmail郵箱(foxmail.com):
POP3服務(wù)器地址:POP.foxmail.com(端口:110)
SMTP服務(wù)器地址:SMTP.foxmail.com(端口:25)
QQ郵箱(mail.qq.com)
POP3服務(wù)器地址:pop.qq.com(端口:110)
SMTP服務(wù)器地址:smtp.qq.com(端口:25)
SMTP服務(wù)器需要身份驗(yàn)證霉囚。
搜狐郵箱(sohu.com):
POP3服務(wù)器地址:pop3.sohu.com(端口:110)
SMTP服務(wù)器地址:smtp.sohu.com(端口:25)
HotMail郵箱(hotmail.com):
POP3服務(wù)器地址:pop.live.com(端口:995)
SMTP服務(wù)器地址:smtp.live.com(端口:587
移動(dòng)139郵箱:
POP3服務(wù)器地址:POP.139.com(端口:110)
SMTP服務(wù)器地址:SMTP.139.com(端口:25)
中華網(wǎng)郵箱(china.com):
POP3服務(wù)器地址:pop.china.com(端口:110)
SMTP服務(wù)器地址:smtp.china.com(端口:25)