SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式,绝大多数语言如C++ Java等都通过库的方式支持了他,当然,go语言也不会例外
很重要
这个一定要写在前面,建议自己尝试的时候呢,多申请几个邮箱.
不要用自己的常用邮箱.
不要用自己的常用邮箱.
不要用自己的常用邮箱.
国内大多数邮箱都有数量限制,单日发的太多容易被封.
smtp库
smtp标准库就是golang实现邮件发送的一个标准库,你可以查看src中的net/stmp
来查看他的源码
stmp库只有两个文件,auth.go与smtp.go,前者是用来验证的,后者是用发发送邮件的,auth.go比较简单,通常也不会自己去实现,所以仅做了解即可,用发是固定的,后面的例子会演示
我们主要来看一下smtp.go中的SendMail()方法,下面为函数签名
func SendMail(addr string, a Auth, from string, to []string, msg []byte) error {}
参数含义如下
addr 邮件服务器地址,一般与端口一起,如腾讯的邮箱 smtp.exmail.qq.com:25
a 通过Auth.go中的PlainAuth()方法验证发件人用户名密码后获得
from 发件人邮箱
to 收件人邮箱列表
msg 邮件内容
利用smtp来发送邮件
smtp库不需要我们视线接口,我们只需要构造好参数,通过SendMail()
来发送邮件即可
我们来模拟向腾讯邮箱中发送一封邮件,首先来构造参数,腾讯邮件服务器地址为smtp.qq.com,端口为25(大部分邮件服务器的端口都是25),常用邮箱的邮件服务器地址我会在最后在备注中贴出
addr := "smtp.qq.com:25"
除此之外,我们还需要构造a,from,to,msg四个参数
首先 使用auth.go中的PlainAuth()来生成a,也就是Auth类型,这边是需要发件人的邮箱,邮件服务器,邮箱密码,我这里演示通过我的一个qq邮箱向我的另外一个qq邮箱发送邮件
获取auth时如果不是TLS连接第一个参数可以给空字符串,我们一般邮件都不需要指定第一个参数,后面参数就分别为 邮箱 密码 邮箱所在的邮件服务器
auth := smtp.PlainAuth("", "你的qq号码@qq.com", "password", "smtp.qq.com")
from发件人也就是上面验证auth用到的邮箱,zhangsheng_1992@sohu.com
from := "你的qq号码@qq.com"
to为收件人,是一个[]string
to := []string{"收件人qq号码@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)
}
注意: 大部分个人邮箱都有smtp保护,所以你可能需要登陆邮箱设置一下
比如qq邮箱你需要设置一个授权码,保护你的密码嘛
点击设置,账户,开启pop3/smtp邮件保护,开启服务的第一个选项,根据提示获取授权码,然后在上面程序密码处填写授权码
而sohu邮箱就简单些,勾选开启服务即可
其他邮箱的设置大同小异,请自行设置,运行程序,然后去邮箱查看
构造邮件头
我门发现上面收到的邮件没有标题,也没有发件人,更没有收件人及内容信息,这是为什么呢?
其实这部分信息在msg中,需要我们来构造,包括
subject 邮件标题
send 邮件接收人,与上面的from区分开,所以我改个名字
receiver 邮件接收人
type 邮件类型 如html邮件 纯文本邮件等等
我门来构造这一部分,注意,这些参数全部时字符串,我们使用fmt.Sprintf()方法来生成这个string,\r\n表示换行,固定格式
subject := fmt.Sprintf("Subject: %s\r\n", "测试邮件")
send := fmt.Sprintf("From: %s 测试发件邮箱\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)
这次我们来看接收到的邮件
是不是信息都全了?
关于邮件头,需要记住
1.它们都是字符串拼接而成的,顺序为
邮件标题 \r\n
发件人 \r\n
收件人 \r\n
邮件类型与字符编码 \r\n\r\n
邮件内容
2.邮件类型与邮件内容之间需要2个换行,即2组\r\n,否则无法看到邮件内容,就如同第一次那样
3.如果接收到的邮件为乱码,那么检查字符编码
发送html邮件
发送html邮件,仅需要设置content_type,也就是邮件类型中的Content-Type: text/html;即:
content_type = "Content-Type: text/html; charset=UTF-8\r\n\r\n" //和邮件内容间两组换行
一般我门不会把html当做一个字符串写到程序中,那样修改起来很麻烦,我们通常的做法是将html写成一个模版,然后读区这个模版,新建一个temp.html,内容如下:
<html>
<head>
<meta charset="utf-8" />
<title>测试邮件</title>
</head>
<body>
<p class="title">我是测试邮件</p>
<p>内容1: %s</p>
<p>内容2: %s</p>
<img src="%s" alt="图片"/>
<p><a href="%s">查看详情</a>
</body>
</html>
然后通过os.Open()打开并读取这个模版
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")//去网上随便找的张图 测试而已,如果你收到的邮件图片打不开请换一个图片链接(随便网上找张图,右键获取图片地址)
其他部分不变,记得设置content_type 编码,再次发送
golang的吉祥物就被我们通过邮件发出去了,当然你还可以增加一些css和js来丰富一封html邮件的,但这不是本文重点
完整代码
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", "测试邮件")
send := fmt.Sprintf("From: %s 测试发件邮箱\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)
}
发送附件
一个标准的邮件包含 邮件头,邮件内容,附件,下面,我就来介绍如何发送一个带附件的邮件
与发送html相同,发送一个带附件的邮件,也需要指定Content-Type参数,并指定一个分隔符,将邮件头,正文部分与附件分隔开,分隔符可以任意设置
content_type := "Content-Type: multipart/mixed; boundary=分隔符\r\n"
分隔符以 --分隔符
开头,以--分隔符--
结尾,然后,为了兼容性,需要将附件编码,为了增加理解,我在代码中增加了过多注释,可能影响阅读体验,建议拷贝下来自己边运行边理解,一定要注意换行!!!!
package main
import (
"encoding/base64"
"fmt"
// "io/ioutil"
"net/smtp"
"os"
)
func main() {
//mailcontent用来保存邮件的有所内容
var mailcontent string
//设置内容间的分隔符 尽量不要用汉字,除此之外设置成什么都可以
delimiter := "module"
//设置头信息头部分
subject := fmt.Sprintf("Subject: %s\r\n", "测试邮件")
send := fmt.Sprintf("From: %s 测试发件邮箱\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"
//邮件描述 这个可以不设置也没关系
content_type += "Content-Description: 我有附件\r\n"
header := subject + send + receiver + content_type
//邮件头部分设置完毕
//分割头信息与普通文本邮件 重要!!!!!!!
mailcontent = header + fmt.Sprintf("\r\n--%s\r\n", delimiter)
//你可以同时发送html 但需要注意分隔 你可以把这段注释掉只发送html邮件
htmlcontent := "Content-Type: text/html; charset=UTF-8\r\n\r\n"
htmlcontent += `<html><head></head><body><h1 style="color:red">我是html内容</h1></body></html>`
mailcontent += htmlcontent
//附件部分
filepath := "/Users/zhangsheng/go/a.png"
filename := "a.png"
//分隔 html内容与附件内容
fileheader := fmt.Sprintf("\r\n--%s\r\n", delimiter)
fileheader += "Content-Type: application/octet-stream\r\n"
fileheader += "Content-Description: 附一个Go文件\r\n"
fileheader += "Content-Transfer-Encoding: base64\r\n"
fileheader += "Content-Disposition: attachment; filename=\"" + filename + "\"\r\n\r\n"
//读取附件内容并编码
file, _ := os.Open(filepath)
fileinfo := make([]byte, 2<<16)//这边需要设置一个足够大的容量来保证可以一次性读取,后面学习ioutil和bufio库的时候会学习到更简便的办法
if n, err := file.Read(fileinfo); err != nil {
fmt.Println(err)
} else {
fileinfo = fileinfo[:n]
}
filecontent := base64.StdEncoding.EncodeToString(fileinfo)
// 我们可以使用ioutil库来实现读取 这样就不用像os Read()设置一个足够大的[]byte
// fileinfo, _ := ioutil.ReadFile(filepath)
// filecontent := make([]byte, base64.StdEncoding.EncodedLen(len(attaData)))
// base64.StdEncoding.Encode(filecontent, attaData)
filemessage := fileheader + filecontent
//分隔结束
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)
}
输出的内容如下:
Subject: 测试邮件
From: 11535xxxxx@qq.com 测试发件邮箱
To: 2974xxxxx@qq.com
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=module 看这里!我设置的分隔符叫module
Content-Description: 这是一封带附档的邮件
--module 第一个分隔符将邮件人信息与邮件内容分隔开
Content-Type: text/html; charset=UTF-8
<html><head></head><body><h1 style="color:red">我是html内容</h1></body></html>
--module 第二个分隔符 将邮件内容与附件分隔开
Content-Type: application/octet-stream
Content-Description: 附一个Png文件
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="a.png"
//这里是很长的一串编码后的结果
--module-- 分隔符结束
如果你要发送多个附件,简单,继续用分隔符分开第一个附件与第二个区分开即可
然后取接收邮件吧
邮件群发
群发邮件也非常简单,无论是简单文件还是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"}
总结
本章我门讲解了smtp库的m,他实现了邮件发送的规则并且可以用来发送邮件,我们讲解了普通邮件,html邮件和附件,以及组合类型邮件,由于邮件和html并不是讲解重点,所以很多东西并没有讲的很细,比如Content-Type,html只是 分隔符 还有包括内嵌邮件等其他使用上的东西
但掌握了smtp库的用法以后,你仅需要取了解这部分内容,并按要求构造你的邮件字符串就可以
附件--常用邮箱地址
注:pop3为收件箱地址,一些客户端邮件管理程序会使用这个
IMAP是另外一种邮件协议 我门常用的都是SMTP协议
阿里云邮箱(mail.aliyun.com):
POP3服务器地址:pop3.aliyun.com(SSL加密端口:995;非加密端口:110)
SMTP服务器地址:smtp.aliyun.com(SSL加密端口:465;非加密端口:25)
IMAP服务器地址:imap.aliyun.com(SSL加密端口:993;非加密端口:143)
谷歌邮箱(google.com):
POP3服务器地址:pop.gmail.com(SSL启用端口:995)
SMTP服务器地址:smtp.gmail.com(SSL启用端口:587)
新浪邮箱(sina.com):
POP3服务器地址:pop3.sina.com.cn(端口:110)
SMTP服务器地址:smtp.sina.com.cn(端口:25)
Tom邮箱(top.com):
POP3服务器地址:pop.tom.com(端口:110)
SMTP服务器地址:smtp.tom.com(端口:25)
网易邮箱(163.com):
POP3服务器地址:pop.163.com(端口:110)
SMTP服务器地址:smtp.163.com(端口:25)
126邮箱:
POP3服务器地址:pop.live.com(端口:995)
SMTP服务器地址:smtp.126.com(端口:25)
雅虎邮箱(yahoo.com):
POP3服务器地址:pop.mail.yahoo.com
SMTP服务器地址:smtp.mail.yahoo.com
雅虎中国(yahoo.com.cn):
POP3服务器地址:pop.mail.yahoo.com.cn(端口:995)
SMTP服务器地址:smtp.mail.yahoo.com.cn(端口:587)
雅虎邮箱POP3的SSL不启用端口为110,POP3的SSL启用端口995;SMTP的SSL不启用端口为25,SMTP的SSL启用端口为465。
Foxmail邮箱(foxmail.com):
POP3服务器地址:POP.foxmail.com(端口:110)
SMTP服务器地址:SMTP.foxmail.com(端口:25)
QQ邮箱(mail.qq.com)
POP3服务器地址:pop.qq.com(端口:110)
SMTP服务器地址:smtp.qq.com(端口:25)
SMTP服务器需要身份验证。
搜狐邮箱(sohu.com):
POP3服务器地址:pop3.sohu.com(端口:110)
SMTP服务器地址:smtp.sohu.com(端口:25)
HotMail邮箱(hotmail.com):
POP3服务器地址:pop.live.com(端口:995)
SMTP服务器地址:smtp.live.com(端口:587
移动139邮箱:
POP3服务器地址:POP.139.com(端口:110)
SMTP服务器地址:SMTP.139.com(端口:25)
中华网邮箱(china.com):
POP3服务器地址:pop.china.com(端口:110)
SMTP服务器地址:smtp.china.com(端口:25)