golang发送带附件的邮件

2018年10月31日 发表评论 阅读评论

之前我写过 python邮件发送模块 ,里面有提到发送带附件的邮件,当时使用python自带的email 模块里已经封装好的包,直接引用即可。在golang下,原生的也有三个对应模块"mime"、"net/mail"、"net/smtp" ,不过这三个模块使用时,不能直接像python里那样简单的套用即可。golang下需要按照smtp协议的规范---RFC531 和MIME规范来生成相应的格式并发出去。

一、有关MIME规范

1、MIME规范

多用途互联网邮件扩展(MIME,Multipurpose Internet Mail Extensions)是一个互联网标准,它扩展了电子邮件标准,使其能够支持:

非ASCII字符文本;
非文本格式附件(二进制、声音、图像等);
由多部分(multiple parts)组成的消息体;
包含非ASCII字符的头信息(Header information)。

这个标准被定义在RFC 2045、RFC 2046、RFC 2047、RFC 2048、RFC 2049等RFC中。MIME改善了由RFC 822转变而来的RFC 2822,这些旧标准规定电子邮件标准并不允许在邮件消息中使用7位ASCII字符集以外的字符。正因如此,一些非英语字符消息和二进制文件,图像,声音等非文字消息原本都不能在电子邮件中传输(MIME可以)。MIME规定了用于表示各种各样的数据类型的符号化方法。此外,在万维网中使用的HTTP协议中也使用了MIME的框架,标准被扩展为互联网媒体类型。

2、带附件邮件发送格式

其发送格式如下:

From:sender_user@361way.com
To:to_user@361way.com
Subject:这是主题
Mime-Version:1.0 //通常是1.0
Content-Type:Multipart/mixed;Boundary="THIS_IS_BOUNDARY_JUST_MAKE_YOURS" //boundary为分界字符,跟http传文件时类似
Date:当前时间
--THIS_IS_BOUNDARY_JUST_MAKE_YOURS         //boundary前边需要加上连接符 -- , 首部和第一个boundary之间有两个空行
Content-Type:text/plain;chart-set=utf-8
                                            //单个部分的首部和正文间有个空行
这是正文1
这是正文2
--THIS_IS_BOUNDARY_JUST_MAKE_YOURS                  //每个部分的与上一部分之间一个空行
Content-Type:image/jpg;name="test.jpg"
Content-Transfer-Encoding:base64
Content-Description:这个是描述
                                            //单个部分的首部和正文间有个空行
base64编码的文件                              //文件内容使用base64 编码,单行不超过80字节,需要插入\r\n进行换行
--THIS_IS_BOUNDARY_JUST_MAKE_YOURS--        //最后结束的标识--boundary--

具体也可以参看你自己foxmail 邮件中查看源代码的信息,如下图:

mime-email

上图是我从一封邮件里提取了部分信息,有兴趣的可以自上而下看下全部信息。

格式书写时,需要注意以下几个问题:

1、头部写的boundary 在下边用的时候要拼上--前缀
2、含有附件的,第一个boundary 和 header 中间有两个空行
3、结束标记为 --boundary--
4、循环文件内容进行插入换行时,如果出现逻辑错误,则传输的附件显示异常,无法正常查看和下载。

二、golang实现

根据网上找到的代码,修改后的代码示例如下:

package main
import (
	"bytes"
	"encoding/base64"
	"fmt"
	"io/ioutil"
	"net"
	"net/smtp"
	"strings"
)
const (
	emlUser = "xcl@xxx.com"
	emlPwd  = "-------"
	emlSMTP = "smtp.xxx.com:25"
)
func main() {
	err := eml()
	if err != nil {
		fmt.Println(" err:", err)
	}
}
// 发普通文本邮件
func eml() error {
	to := "xcl@xxx.com"
	cc := "cc@xxx.com"
	sendTo := strings.Split(to, ";")
	subject := "test mail for 361way.com"
	boundary := "ds13difsknfsifuere134" //boundary 用于分割邮件内容,可自定义. 注意它的开始和结束格式
	mime := bytes.NewBuffer(nil)
	//设置邮件
	mime.WriteString(fmt.Sprintf("From: %s<%s>\r\nTo: %s\r\nCC: %s\r\nSubject: %s\r\nMIME-Version: 1.0\r\n", emlUser, emlUser, to, cc, subject))
	mime.WriteString(fmt.Sprintf("Content-Type: multipart/mixed; boundary=%s\r\n", boundary))
	mime.WriteString("Content-Description: 这是一封带附档的邮件\r\n")
	//邮件普通Text正文
	mime.WriteString(fmt.Sprintf("--%s\r\n", boundary))
	mime.WriteString("Content-Type: text/plain; charset=utf-8\r\n")
	mime.WriteString("This is a multipart message in MIME format.")
	// 第一个附件
	attaFile := "/Users/xcl/xclcode/tconv.go"
	attaFileName := "tconv.go"
	mime.WriteString(fmt.Sprintf("\n--%s\r\n", boundary))
	mime.WriteString("Content-Type: application/octet-stream\r\n")
	mime.WriteString("Content-Description: 附一个Go文件\r\n")
	mime.WriteString("Content-Transfer-Encoding: base64\r\n")
	mime.WriteString("Content-Disposition: attachment; filename=\"" + attaFileName + "\"\r\n\r\n")
	//读取并编码文件内容
	attaData, err := ioutil.ReadFile(attaFile)
	if err != nil {
		return err
	}
	b := make([]byte, base64.StdEncoding.EncodedLen(len(attaData)))
	base64.StdEncoding.Encode(b, attaData)
	mime.Write(b)
	//第二个附件
	mime.WriteString(fmt.Sprintf("\r\n--%s\r\n", boundary))
	mime.WriteString("Content-Type: text/plain\r\n")
	mime.WriteString("Content-Description: 附一个Text文件\r\n")
	mime.WriteString("Content-Disposition: attachment; filename=\"test.txt\"\r\n\r\n")
	mime.WriteString("this is the attachment text") //这里写入的是附件test.txt的内容
	//邮件结束
	mime.WriteString("\r\n--" + boundary + "--\r\n\r\n")
	fmt.Println(mime.String())
	//发送相关
	smtpHost, _, err := net.SplitHostPort(emlSMTP)
	if err != nil {
		return err
	}
	auth := smtp.PlainAuth("", emlUser, emlPwd, smtpHost)
	return smtp.SendMail(emlSMTP, auth, emlUser, sendTo, mime.Bytes())
}

三、第三方模块

虽然在golang的原生模块里是没有简洁方便的附件调用发送方法,但在很多第三方模块里都已经很好的进行了封装,这里列这个第三方模块,具体也可以参看下他们实现的方式。

https://github.com/scorredoira/email

https://github.com/jordan-wright/email

https://github.com/go-gomail/gomail/




本站的发展离不开您的资助,金额随意,欢迎来赏!

You can donate through PayPal.
My paypal id: itybku@139.com
Paypal page: https://www.paypal.me/361way

  1. 本文目前尚无任何评论.
  1. 本文目前尚无任何 trackbacks 和 pingbacks.