go发送邮件

我傻笑你跑掉 · · 3203 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

SMTP(Simple Mail Transfer Protocol)即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则,由它来控制信件的中转方式,绝大多数语言如C++ Java等都通过库的方式支持了他,当然,go语言也不会例外

很重要

这个一定要写在前面,建议自己尝试的时候呢,多申请几个邮箱.
不要用自己的常用邮箱.
不要用自己的常用邮箱.
不要用自己的常用邮箱.
国内大多数邮箱都有数量限制,单日发的太多容易被封.

smtp库

smtp标准库就是golang实现邮件发送的一个标准库,你可以查看src中的net/stmp来查看他的源码

stmp库只有两个文件,auth.gosmtp.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邮件保护,开启服务的第一个选项,根据提示获取授权码,然后在上面程序密码处填写授权码


qqmail.png

而sohu邮箱就简单些,勾选开启服务即可


sohumail.png

其他邮箱的设置大同小异,请自行设置,运行程序,然后去邮箱查看


receivemail.png

构造邮件头

我门发现上面收到的邮件没有标题,也没有发件人,更没有收件人及内容信息,这是为什么呢?

其实这部分信息在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)

这次我们来看接收到的邮件

receivemail2.png

是不是信息都全了?

关于邮件头,需要记住
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-- 分隔符结束

如果你要发送多个附件,简单,继续用分隔符分开第一个附件与第二个区分开即可

然后取接收邮件吧


attr.png
htmlattrmail.png

邮件群发

群发邮件也非常简单,无论是简单文件还是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)


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:我傻笑你跑掉

查看原文:go发送邮件

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

3203 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传