<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
書接上文郵件實現詳解,這裡我們及我們簡單複習一下smtp的指令如下:
telnet smtp.163.com 25 [outpout] ehlo dz45693 [outpout] auth login [outpout] 輸入使用者名稱base64 [outpout] 輸入密碼base64 mail from:<dz45693@163.com> [outpout] rcpt to:<dz45693@sina.com> [outpout] data [outpout] from:<dz45693@163.com> to:<dz45693@sina.com> subject:hello world This is the first email sent by hand using the SMTP protocol quit
好,那我們下現在用go實現程式碼讓如下:這裡只是一個demo,主要熟悉smtp命令
package main import ( "bufio" "encoding/base64" "fmt" "net" "strconv" "strings" ) func main() { testSmtp() } var gConn net.Conn var gRead *bufio.Reader var gWrite *bufio.Writer //可以放到這樣的類裡 type TcpClient struct { Conn net.Conn Read *bufio.Reader Write *bufio.Writer } // func Connect(host string, port int) (net.Conn, *bufio.Reader, *bufio.Writer) { addr := host + ":" + strconv.Itoa(port) conn, err := net.Dial("tcp", addr) if err != nil { return nil, nil, nil } reader := bufio.NewReader(conn) writer := bufio.NewWriter(conn) return conn, reader, writer } // //收取一行,可再優化 func RecvLine() string { line, err := gRead.ReadString('n') //如何設定超時? if err != nil { fmt.Print(err) return "" } line = strings.Split(line, "r")[0] //還要再去掉 "r",其實不去掉也可以 return line } func SendLine(line string) { gWrite.WriteString(line + "rn") gWrite.Flush() } //解碼一行命令,這裡比較簡單就是按空格進行分隔就行了 func DecodeCmd(line string, sp string) []string { tmp := strings.Split(line, sp) var cmds = []string{"", "", "", "", ""} //先定義多幾個,以面後面使用時產生異常 for i := 0; i < len(tmp); i++ { if i >= len(cmds) { break } cmds[i] = tmp[i] } return cmds } //讀取多行結果 func RecvMCmd() string { i := 0 rs := "" mLine := "" for i = 0; i < 50; i++ { rs = RecvLine() //只收取一行 mLine = mLine + rs + "rn" if len(rs) < 4 { break } //長度要足夠 c4 := rs[4-1] //第4個字元 if ' ' == c4 { break } //第4個字元是空格就表示讀取完了//也可以判斷 "250[空格]" } return mLine } //簡單的測試一下 smtp func testSmtp() { //連線 gConn, gRead, gWrite = Connect("smtp.163.com", 25) defer gConn.Close() //收取一行 line := RecvLine() fmt.Println("recv:" + line) //解碼一下,這樣後面的 EHLO 才能有正確的第二個引數 cmds := DecodeCmd(line, " ") domain := cmds[1] //要從對方的應答中取出域名//空格分開的各個命令引數中的第二個 //傳送一個命令 SendLine("EHLO" + " " + domain) //domain 要求其實來自 HELO 命令//HELO <SP> <domain> <CRLF> //收取多行 line = RecvMCmd() fmt.Println("recv:" + line) //-------------------------------------------------- //用 base64 登入 SendLine("AUTH LOGIN") //收取一行 line = RecvLine() fmt.Println("recv:" + line) s := "dz45693" //要換成你的使用者名稱,注意 163 郵箱的話不要帶後面的 @域名 部分 s = base64.StdEncoding.EncodeToString([]byte(s)) SendLine(s) //收取一行 line = RecvLine() fmt.Println("recv:" + line) s = "xxxxx" //要換成您的密碼 s = base64.StdEncoding.EncodeToString([]byte(s)) SendLine(s) //收取一行 line = RecvLine() fmt.Println("recv:" + line) //-------------------------------------------------- //郵件內容 from := "dz45693@163.com" to := "dz45693@sina.com" SendLine("MAIL FROM: <" + from + ">") //注意"<" 符號和前面的空格。空格在協定中有和沒有都可能,最好還是有 //收取一行 line = RecvLine() fmt.Println("recv:" + line) SendLine("RCPT TO: <" + to + ">") //收取一行 line = RecvLine() fmt.Println("recv:" + line) SendLine("DATA") //收取一行 line = RecvLine() fmt.Println("recv:" + line) //傳送郵件頭 SendLine("from:<dz45693@163.com>") SendLine("to:<dz45693@sina.com>") SendLine("subject:hello world") SendLine("") //傳送空行 後面就是郵件體 SendLine("This is the first email sent by hand using the SMTP protocol") SendLine(".") //郵件結束符 //收取一行 line = RecvLine() fmt.Println("recv:" + line) SendLine("quit") //連結推出 line = RecvLine() fmt.Println("recv:" + line) } //
執行結果如下:
在go的sdk中提供了SendMail方法【傳送郵件後這個方法會關閉連結】,實現如下:
實現如下:
func SendMailBySmtp(){ auth := smtp.PlainAuth("", "dz45693@163.com", "xxx", "smtp.163.com") to := []string{"dz45693@sina.com"} image,_:=ioutil.ReadFile("d:\Downloads\1.png") imageBase64:=base64.StdEncoding.EncodeToString(image) msg := []byte("from:dz45693@163.comrn"+ "to: dz45693@sina.comrn" + "Subject: hello,subject!rn"+ "Content-Type:multipart/mixed;boundary=arn"+ "Mime-Version:1.0rn"+ "rn" + "--arn"+ "Content-type:text/plain;charset=utf-8rn"+ "Content-Transfer-Encoding:quoted-printablern"+ "rn"+ "此處為正文內容!rn"+ "--arn"+ "Content-type:image/jpg;name=1.jpgrn"+ "Content-Transfer-Encoding:base64rn"+ "rn"+ imageBase64+"rn"+ "--a--rn") err := smtp.SendMail("smtp.163.com:25", auth, "dz45693@163.com", to, msg) if err != nil { fmt.Println(err) } }
執行效果:
使用第三方庫gomail實現郵件的傳送更多瞭解,
請前往:https://pkg.go.dev/gopkg.in/gomail.v2?utm_source=godoc
範例如下:
func SendMailByGomailOne(){ m := gomail.NewMessage() m.SetAddressHeader("From", "dz45693@163.com", "dz45693") m.SetHeader("To", "dz45693@sina.com") m.SetHeader("Subject", "hello SendMailByGomailOne!") m.Embed("d:\Downloads\1.png") m.SetBody("text/html", "此處為正文121333!") d := gomail.NewDialer("smtp.163.com", 25, "dz45693@163.com", "xxxx") if err := d.DialAndSend(m); err != nil { panic(err) } }
執行結果:
來我們看看DialAndSend的實現如下:
package gomail import ( "crypto/tls" "fmt" "io" "net" "net/smtp" "strings" "time" ) // A Dialer is a dialer to an SMTP server. type Dialer struct { // Host represents the host of the SMTP server. Host string // Port represents the port of the SMTP server. Port int // Username is the username to use to authenticate to the SMTP server. Username string // Password is the password to use to authenticate to the SMTP server. Password string // Auth represents the authentication mechanism used to authenticate to the // SMTP server. Auth smtp.Auth // SSL defines whether an SSL connection is used. It should be false in // most cases since the authentication mechanism should use the STARTTLS // extension instead. SSL bool // TSLConfig represents the TLS configuration used for the TLS (when the // STARTTLS extension is used) or SSL connection. TLSConfig *tls.Config // LocalName is the hostname sent to the SMTP server with the HELO command. // By default, "localhost" is sent. LocalName string } // NewDialer returns a new SMTP Dialer. The given parameters are used to connect // to the SMTP server. func NewDialer(host string, port int, username, password string) *Dialer { return &Dialer{ Host: host, Port: port, Username: username, Password: password, SSL: port == 465, } } // NewPlainDialer returns a new SMTP Dialer. The given parameters are used to // connect to the SMTP server. // // Deprecated: Use NewDialer instead. func NewPlainDialer(host string, port int, username, password string) *Dialer { return NewDialer(host, port, username, password) } // Dial dials and authenticates to an SMTP server. The returned SendCloser // should be closed when done using it. func (d *Dialer) Dial() (SendCloser, error) { conn, err := netDialTimeout("tcp", addr(d.Host, d.Port), 10*time.Second) if err != nil { return nil, err } if d.SSL { conn = tlsClient(conn, d.tlsConfig()) } c, err := smtpNewClient(conn, d.Host) if err != nil { return nil, err } if d.LocalName != "" { if err := c.Hello(d.LocalName); err != nil { return nil, err } } if !d.SSL { if ok, _ := c.Extension("STARTTLS"); ok { if err := c.StartTLS(d.tlsConfig()); err != nil { c.Close() return nil, err } } } if d.Auth == nil && d.Username != "" { if ok, auths := c.Extension("AUTH"); ok { if strings.Contains(auths, "CRAM-MD5") { d.Auth = smtp.CRAMMD5Auth(d.Username, d.Password) } else if strings.Contains(auths, "LOGIN") && !strings.Contains(auths, "PLAIN") { d.Auth = &loginAuth{ username: d.Username, password: d.Password, host: d.Host, } } else { d.Auth = smtp.PlainAuth("", d.Username, d.Password, d.Host) } } } if d.Auth != nil { if err = c.Auth(d.Auth); err != nil { c.Close() return nil, err } } return &smtpSender{c, d}, nil } func (d *Dialer) tlsConfig() *tls.Config { if d.TLSConfig == nil { return &tls.Config{ServerName: d.Host} } return d.TLSConfig } func addr(host string, port int) string { return fmt.Sprintf("%s:%d", host, port) } // DialAndSend opens a connection to the SMTP server, sends the given emails and // closes the connection. func (d *Dialer) DialAndSend(m ...*Message) error { s, err := d.Dial() if err != nil { return err } defer s.Close() return Send(s, m...) } type smtpSender struct { smtpClient d *Dialer } func (c *smtpSender) Send(from string, to []string, msg io.WriterTo) error { if err := c.Mail(from); err != nil { if err == io.EOF { // This is probably due to a timeout, so reconnect and try again. sc, derr := c.d.Dial() if derr == nil { if s, ok := sc.(*smtpSender); ok { *c = *s return c.Send(from, to, msg) } } } return err } for _, addr := range to { if err := c.Rcpt(addr); err != nil { return err } } w, err := c.Data() if err != nil { return err } if _, err = msg.WriteTo(w); err != nil { w.Close() return err } return w.Close() } func (c *smtpSender) Close() error { return c.Quit() } // Stubbed out for tests. var ( netDialTimeout = net.DialTimeout tlsClient = tls.Client smtpNewClient = func(conn net.Conn, host string) (smtpClient, error) { return smtp.NewClient(conn, host) } ) type smtpClient interface { Hello(string) error Extension(string) (bool, string) StartTLS(*tls.Config) error Auth(smtp.Auth) error Mail(string) error Rcpt(string) error Data() (io.WriteCloser, error) Quit() error Close() error }
DialAndSend ,首先呼叫Dial方法建立連線,然後傳送郵件,最後關閉連結,如果要頻繁發郵件,那麼是否保持長連線更好了?這裡的Dial 呼叫了smtp.NewClient 建立smtp.Client物件c,然後呼叫c.Hello ,c.Auth,send 實際是呼叫c.Mail,c.Rcpt,c.Data,那麼我們可以自己呼叫Dial方法 然後迴圈呼叫send方法,最後在close。
程式碼如下:
func SendMailByGomailTwo() { d := gomail.NewDialer("smtp.163.com", 25, "dz45693@163.com", "xxxx") m := gomail.NewMessage() m.SetAddressHeader("From", "dz45693@163.com", "dz45693") m.SetHeader("To", "dz45693@sina.com") m.SetHeader("Subject", "hello SendMailByGomailtwo!") m.Embed("d:\Downloads\1.png") m.SetBody("text/html", "此處為正文121333!SendMailByGomailtwo") s, err := d.Dial() if err != nil { panic(err) } defer s.Close() err = gomail.Send(s, m) if err != nil { panic(err) } m.Reset() m.SetAddressHeader("From", "dz45693@163.com", "dz45693") m.SetHeader("To", "dz45693@sina.com") m.SetHeader("Subject", "hello SendMailByGomailthree!") m.Embed("d:\Downloads\2.png") m.SetBody("text/html", "此處為正文1SendMailByGomailthreeSendMailByGomailthree!") err = gomail.Send(s, m) if err != nil { panic(err) } }
執行結果:
以上就是go smtp實現郵件傳送範例詳解的詳細內容,更多關於go smtp郵件傳送的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45