20 Star 167 Fork 28

qiqi/orange

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
smtp.go 7.56 KB
一键复制 编辑 原始数据 按行查看 历史
qiqi 提交于 2020-01-25 01:19 . 添加邮件发送工具
package mailer
import (
"encoding/base64"
"bytes"
"mime/quotedprintable"
"path"
"io/ioutil"
"strconv"
"net/smtp"
"errors"
"time"
"fmt"
"strings"
"crypto/tls"
"net"
)
type Mailer struct {
Host string
Port int
UserName string
Password string
OpenTsl bool
}
// MiniType类型枚举
var miniTypeMap = map[string]string{
".jpg":"image/jpeg",
".jpeg":"image/jpeg",
".png":"image/png",
".gif":"image/gif",
".bmp":"application/x-bmp",
}
type attachment struct {
Name string
MiniType string
ContentId string
Content string
}
type sendMail struct{
option Mailer // 邮箱配置信息
from string // 发送人名称 默认是发送邮箱
to []string // 收件人
cc []string // 抄送人
bcc []string // 密送人
subject string // 邮件标题
body string // 邮件内容
bodyType string // 邮件正文类下 html/plain
attachments []attachment
}
// SetType 设置邮件类型 html/plain
func (sm *sendMail) SetType(bodyType string) (*sendMail){
sm.bodyType = bodyType
return sm
}
// SetHtml 设置html格式邮件类型
func (sm *sendMail) SetHtml() (*sendMail){
return sm.SetType("html")
}
// SetText 设置文本邮件类型
func (sm *sendMail) SetText() (*sendMail){
return sm.SetType("plain")
}
// From 添加发送人名称
func (sm *sendMail) From(fromuser string) (*sendMail){
sm.from = fromuser
return sm
}
// To 添加收件人
func (sm *sendMail) To(touser []string) (*sendMail){
sm.to = touser
return sm
}
// Cc 添加抄送人
func (sm *sendMail) Cc(ccuser []string) (*sendMail){
sm.cc = ccuser
return sm
}
// Bcc 添加密送人
func (sm *sendMail) Bcc(bccuser []string) (*sendMail){
sm.bcc = bccuser
return sm
}
// Subject添加邮件主题
func (sm *sendMail) Subject(subject string) (*sendMail){
sm.subject = subject
return sm
}
// byte2Base64将byte数据转base64
// rfc2045中要求base64一行不要超过76个字符,超过需要添加换行符 https://tools.ietf.org/html/rfc2045
func (sm *sendMail)byte2Base64(strByte []byte) string{
strByteLen := len(strByte)
payload := make([]byte, base64.StdEncoding.EncodedLen(strByteLen))
base64.StdEncoding.Encode(payload, strByte)
stream := ""
for index, size := 0, len(payload); index < size; index++ {
stream += string(payload[index])
if (index+1)%76 == 0 {
stream += "\r\n"
}
}
return stream
}
// quotedEncode 将字符串进行quoted-printable编码
func (sm *sendMail)quotedEncode(str string) string{
buffer := bytes.NewBuffer(nil)
w := quotedprintable.NewWriter(buffer)
w.Write([]byte(str))
w.Close()
return buffer.String()
}
// 添加附件
func (sm *sendMail) AddAttachment(filePath string) (contentId string, err error){
fileName := path.Base(filePath)
fileExt := path.Ext(filePath)
fileMiniType := "application/octet-stream"
if _,ok := miniTypeMap[fileExt]; ok {
fileMiniType = miniTypeMap[fileExt]
}
fileBytes, err := ioutil.ReadFile(filePath)
if err != nil {
return contentId, err
}
contentId = "qqivy"+strconv.Itoa(len(sm.attachments) + 1)+"@orange.generate"
contentString:= sm.byte2Base64(fileBytes)
attachmentInfo := attachment{
Name:fileName,
MiniType:fileMiniType,
ContentId:contentId ,
Content:contentString,
}
sm.attachments = append(sm.attachments, attachmentInfo)
return contentId, nil
}
// 发送邮件
func (sm *sendMail) Send(message string) (err error) {
err = sm.validata()
if err != nil{
return err
}
// Set up authentication information.
auth := smtp.PlainAuth("", sm.option.UserName, sm.option.Password, sm.option.Host)
buffer := bytes.NewBuffer(nil)
boudary := "THIS_IS_BOUNDARY_ORANGE"
sm.body = message
sm.writeHeader(buffer, boudary)
sm.writeBody(buffer, boudary)
sm.writeAttachment(buffer, boudary)
buffer.WriteString("\r\n--" + boudary + "--\r\n")
toUsers := make([]string, len(sm.to) + len(sm.cc) + len(sm.bcc))
toUsers = append(sm.to, sm.cc...)
toUsers = append(toUsers, sm.bcc...)
addr:=fmt.Sprintf("%s:%d",sm.option.Host, sm.option.Port)
if sm.option.OpenTsl == true {
err = sm.option.sendMailByTslDo(addr, auth, sm.option.UserName, toUsers, buffer.Bytes())
}else{
err = smtp.SendMail(addr, auth, sm.option.UserName, toUsers, buffer.Bytes())
}
return err
}
// sendMailByTslDo
func (mail *Mailer)sendMailByTslDo(addr string, auth smtp.Auth, from string,
to []string, msg []byte) (err error) {
//create smtp client
c, err := mail.TlsDial(addr)
if err != nil {
return err
}
defer c.Close()
if auth != nil {
if ok, _ := c.Extension("AUTH"); ok {
if err = c.Auth(auth); err != nil {
return err
}
}
}
if err = c.Mail(from); err != nil {
return err
}
for _, addr := range to {
if err = c.Rcpt(addr); err != nil {
return err
}
}
w, err := c.Data()
if err != nil {
return err
}
_, err = w.Write(msg)
if err != nil {
return err
}
err = w.Close()
if err != nil {
return err
}
return c.Quit()
}
//return a smtp client by TLS connect
func (mail *Mailer)TlsDial(addr string) (*smtp.Client, error) {
conn, err := tls.Dial("tcp", addr, nil)
if err != nil {
return nil, err
}
//分解主机端口字符串
host, _, _ := net.SplitHostPort(addr)
return smtp.NewClient(conn, host)
}
// writeAttachment 构建smtp邮件附件
func (sm *sendMail) writeAttachment(buffer *bytes.Buffer, boudary string) (attachment string) {
for _, item:=range sm.attachments{
attachment = "\r\n--" + boudary + "\r\n"
attachment += "Content-Type: " + item.MiniType + "; name=" + item.Name + "\r\n"
attachment += "Content-Transfer-Encoding: base64\r\n"
attachment += "Content-ID: <" + item.ContentId +">\r\n"
if item.MiniType != "application/octet-stream"{
attachment += "Content-Disposition: inline; filename="+ item.Name+ "\r\n"
}
attachment += "\r\n"
attachment += item.Content
attachment += "\r\n"
buffer.WriteString(attachment)
}
return attachment
}
// writeBody 构建smtp邮件正文
func (sm *sendMail) writeBody(buffer *bytes.Buffer, boudary string) string {
body := "\r\n--" + boudary + "\r\n"
body += "Content-Type: text/"+sm.bodyType+"; charset=utf-8\r\n"
body += "Content-Transfer-Encoding: quoted-printable\r\n"
body += "\r\n" + sm.quotedEncode(sm.body) +"\r\n\r\n"
buffer.WriteString(body)
return body
}
// writeHeader 构建smtp邮件头部
func (sm *sendMail) writeHeader(buffer *bytes.Buffer, boudary string) string {
header := "Date: "+ time.Now().Format(time.RFC1123Z) +"\r\n"
header += "Subject: "+sm.subject+"\r\n"
header += fmt.Sprintf("From: %s <%s>\r\n", "=?utf-8?Q?"+sm.quotedEncode(sm.from)+"?=", sm.option.UserName)
header += "To: "+ strings.Join(sm.to, ", ") +"\r\n"
if len(sm.cc) > 0{
header += "Cc: "+ strings.Join(sm.cc, ", ") +"\r\n"
}
if len(sm.bcc) > 0{
header += "Bcc: "+ strings.Join(sm.bcc, ", ") +"\r\n"
}
header += "MIME-Version: 1.0\r\n"
header += "Content-Type: multipart/related;\r\n boundary=\""+ boudary +"\"\r\n"
headerString := header
headerString += "\r\n"
buffer.WriteString(headerString)
return headerString
}
// validata 检测发送信息有效性
func (sm *sendMail)validata() (err error){
if sm.subject == ""{
return errors.New("mail subject is empty")
}
if len(sm.to) == 0{
return errors.New("to user addr is empty")
}
return
}
// 获取发送邮件对象
func GetSendMailer(mailOption Mailer) (ret *sendMail, err error){
if mailOption.Host == "" || mailOption.Port == 0 ||
mailOption.UserName == "" || mailOption.Password == ""{
return ret, errors.New("mail option have an error")
}
ret = &sendMail{
option: mailOption,
from: mailOption.UserName, // 配置默认发送人
bodyType: "html",
}
// 465端口自动开启tsl
if mailOption.Port == 465{
ret.option.OpenTsl = true
}
return ret, nil
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
Go
1
https://gitee.com/zhucheer/orange.git
git@gitee.com:zhucheer/orange.git
zhucheer
orange
orange
v0.2.6

搜索帮助