golang封装发送QQ、163邮件

开通POP3/SMTP/IMAP服务

在这里插入图片描述

代码部分

安装email

 go get github.com/jordan-wright/email

yaml配置

email:
  account: 邮箱账号
  password: 授权码
  host: smtp.163.com
  port: 25
  # 授权类型,163用这个OK,我outlock用的自定义的login
  auth: plain

初始化连接池,可以参考这个文档

https://www.topgoer.com/%E5%85%B6%E4%BB%96/%E5%8F%91%E9%82%AE%E4%BB%B6.html

initialize.go

package initilize

import (
	"crypto/tls"
	"fmt"
	"log"
	"net/smtp"

	"xxx/dmp/demo/common/global"
	"github.com/jordan-wright/email"
)

/**
 * 初始化邮件客户端
 */
func InitilizeEmail() {
	// 创建连接池
	p, err := email.NewPool(
		fmt.Sprintf("%s:%d", global.CONFIG.Email.Host, global.CONFIG.Email.Port),
		4,
		smtp.PlainAuth("", global.CONFIG.Email.Account, global.CONFIG.Email.Password, global.CONFIG.Email.Host),
		&tls.Config{
			InsecureSkipVerify: true, // 忽略证书验证(慎用)
			ServerName:         global.CONFIG.Email.Host,
		},
	)
	if err != nil {
		log.Fatal("创建邮箱客户端连接池失败:", err)
	}
	global.EmailPool = p
	fmt.Println("创建邮箱客户端连接池成功")
}

代码封装

loginAuth.go

package email

import (
	"errors"
	"net/smtp"

	"xxx/common/global"
)

type loginAuth struct {
	username, password string
}

func LoginAuth(username, password string) smtp.Auth {
	return &loginAuth{username, password}
}

func (a *loginAuth) Start(server *smtp.ServerInfo) (string, []byte, error) {
	return "LOGIN", []byte{}, nil
}

func (a *loginAuth) Next(fromServer []byte, more bool) ([]byte, error) {
	if more {
		switch string(fromServer) {
		case "Username:":
			return []byte(a.username), nil
		case "Password:":
			return []byte(a.password), nil
		default:
			return nil, errors.New("unkown fromServer")
		}
	}
	return nil, nil
}

func GetAuth() smtp.Auth {
	switch global.CONFIG.Email.Auth {
	case "login":
		return LoginAuth(global.CONFIG.Email.Account, global.CONFIG.Email.Password)
	case "cramMD5":
		return smtp.CRAMMD5Auth(global.CONFIG.Email.Account, global.CONFIG.Email.Password)
	default:
		return smtp.PlainAuth("", global.CONFIG.Email.Account, global.CONFIG.Email.Password, global.CONFIG.Email.Host)
	}
}

email.go

package email

import (
	"crypto/tls"
	"errors"
	"fmt"
	"os"
	"sync"

	"xxx/common/global"
	"xxx/util"

	"github.com/gin-gonic/gin"
	"github.com/jordan-wright/email"
)

const (
	// 邮箱绑定
	EmailTypeBind = 1
)

var topics = map[int]string{
	EmailTypeBind:          "邮箱绑定",
}
var templates = map[int]string{
	EmailTypeBind:          "bind_email.tmpl",
}

var emailServiceRegistry = struct {
	services map[int]*EmailService
	sync.Mutex
}{
	services: make(map[int]*EmailService),
}

var background, logo, floor string

func init() {
	background, _ = util.ImageToBase64("./static/email/background.png")
	logo, _ = util.ImageToBase64("./static/email/logo.png")
	floor, _ = util.ImageToBase64("./static/email/floor.png")
}

type EmailService struct {
	Email           *email.Email
	TemplateContent []byte
}

func GetEmailInstance(t int) (*EmailService, error) {
	topic, ok := topics[t]
	temp, ok2 := templates[t]
	if !ok || !ok2 {
		return nil, errors.New("主题不存在")
	}

	emailServiceRegistry.Lock()
	defer emailServiceRegistry.Unlock()

	// 检查注册树中是否已经存在对应类型的 EmailService
	if service, exists := emailServiceRegistry.services[t]; exists {
		return service, nil
	}

	e := email.NewEmail()
	e.From = "System <" + global.CONFIG.Email.Account + ">"
	e.Subject = topic

	// 读取模板文件内容
	templateFile := "./client/email/template/" + temp
	templateContent, err := os.ReadFile(templateFile)
	if err != nil {
		return nil, errors.New("模板不存在")
	}

	service := &EmailService{
		Email:           e,
		TemplateContent: templateContent,
	}

	// 将创建的 EmailService 注册到注册树中
	emailServiceRegistry.services[t] = service

	return service, nil
}

func (e *EmailService) Send(to []string, data any) error {
	// 解析模板
	tmpl, err := util.GenerateHTMLTemplate(string(e.TemplateContent), data)
	if err != nil {
		return err
	}

	e.Email.To = to
	e.Email.HTML = tmpl.Bytes()

	return e.Email.SendWithStartTLS(fmt.Sprintf("%s:%d", global.CONFIG.Email.Host, global.CONFIG.Email.Port), GetAuth(),
		&tls.Config{
			InsecureSkipVerify: true, // 忽略证书验证(慎用)
			ServerName:         global.CONFIG.Email.Host,
		})
	// 使用连接池会报错 read tcp xx.xx.xx.xx:xx->xx.xx.xx.xx:xx: read: connection reset by peer
	// 是的,您需要设置重试 - 在这种情况下,远程服务器会重置连接,这可能表明存在防火墙、垃圾邮件过滤器或任何其他问题。
	// return global.EmailPool.Send(e.Email, 30*time.Second)
}

type PublicData struct {
	FloorImg      string
	BackgroundImg string
	LogoImg       string
}

// 绑定
type BindEmailData struct {
	PublicData
	Code      string
	UserName  string
	ValidTime int
}

func GetPublicData() PublicData {
	return PublicData{
		FloorImg:      "data:image/png;base64," + floor,
		BackgroundImg: "data:image/png;base64," + background,
		LogoImg:       "data:image/png;base64," + logo,
	}
}

func SendBindEmail(ctx *gin.Context, to string, userName string, code string, validTime int) error {
	instance, err := GetEmailInstance(EmailTypeBind)
	if err != nil {
		return err
	}
	return instance.Send([]string{to}, BindEmailData{
		PublicData: GetPublicData(),
		Code:       code,
		UserName:   userName,
		ValidTime:  validTime,
	})
}

自定义模板

bind_email.tmpl

<html>

<body>
    <h1>验证码: {{ .Code }}</h1>
</body>

</html>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Golang中使用`http`包发送一段字符串,可以使用`http.Post`函数。以下是一个发送字符串的示例代码: ```go package main import ( "bytes" "fmt" "net/http" ) func main() { url := "http://example.com" // 替换为实际的URL message := "Hello, World!" // 要发送的字符串 // 创建一个包含字符串的字节缓冲区 body := bytes.NewBufferString(message) // 发送POST请求 resp, err := http.Post(url, "text/plain", body) if err != nil { fmt.Println("发送请求时出错:", err) return } defer resp.Body.Close() // 检查响应状态码 if resp.StatusCode != http.StatusOK { fmt.Println("请求返回非200状态码:", resp.StatusCode) return } fmt.Println("请求成功") } ``` 在这个示例中,我们将URL替换为要发送请求的实际URL,并将`message`变量设置为要发送的字符串。然后,我们使用`bytes.NewBufferString`函数创建一个`bytes.Buffer`类型的对象,该对象包含要发送的字符串。 接下来,我们使用`http.Post`函数发送一个POST请求。我们将URL、Content-Type(在这个示例中为"text/plain")和请求体作为参数传递给`http.Post`函数。该函数将返回一个响应对象`resp`和一个可能的错误。 最后,在处理完响应后,我们关闭响应主体(`resp.Body.Close()`)并检查响应的状态码(在这个示例中,我们只检查是否为200状态码)。 请注意,示例代码中的URL和要发送的字符串都需要根据实际情况进行替换。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值