1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

GoでSMTPを使用してtextとhtmlをマルチパートにメール送信

Posted at

既存のライブラリとかを色々調べた結果、うーんという感じだったので実装

テキストメールとHTMLメールの文字列を受け取って
・両方とも存在すればマルチパートで配信
・どちらか一方が空文字だったらマルチパートにはしないようにする。

※添付ファイルとかには未対応。

改めて、メールの作り方の勉強になりました。

参考(大部分を流用させて頂いてます。)
https://qiita.com/yamasaki-masahide/items/a9f8b43eeeaddbfb6b44

// SendMailAtSMTP SMTPを使用してメール送信
// textBody テキストメールの内容
// htmlBody HTMLメールの内容
func (serv *SendMailService) SendMailAtSMTP(subject, textBody, htmlBody string, toAddresses []string) error {

	c, err := smtp.Dial("localhost:25")
	if err != nil {
		return err
	}

	for _, email := range toAddresses {
		c.Mail(test@localhost) // 送り主
		c.Rcpt(email)       // 受信者

		wc, err := c.Data()
		if err != nil {
			return err
		}
		uuid := "乱数" // 何かしらで乱数生成

		var textBodyBuffer bytes.Buffer
		textBodyBuffer.WriteString(textBody)

		var htmlBodyBuffer bytes.Buffer
		htmlBodyBuffer.WriteString(htmlBody)

		var message bytes.Buffer
		boundary := ""

		// mail header
		message.WriteString("To: " + email + "\r\n")
		message.WriteString(encodeSubject(subject))
		message.WriteString("Mime-Version: 1.0\r\n")
		if textBody != "" && htmlBody != "" {
			message.WriteString("Content-Type: multipart/alternative; boundary=\"" + uuid + "\"\r\n")
			message.WriteString("\r\n")

			boundary = "--" + uuid + "\r\n"
		}

		// text/plain mail body
		if textBody != "" {
			message.WriteString(boundary)
			message.WriteString("Content-Type: text/plain; charset=\"UTF-8\"\r\n")
			message.WriteString("Content-Transfer-Encoding: base64\r\n")
			message.WriteString("\r\n")
			message.WriteString(add76crlf(base64.StdEncoding.EncodeToString(textBodyBuffer.Bytes())))
			message.WriteString("\r\n")
			message.WriteString("\r\n")
		}

		// text/html mail body
		if htmlBody != "" {
			message.WriteString(boundary)
			message.WriteString("Content-Type: text/html; charset=\"UTF-8\"\r\n")
			message.WriteString("Content-Transfer-Encoding: base64\r\n")
			message.WriteString("\r\n")
			message.WriteString(add76crlf(base64.StdEncoding.EncodeToString(htmlBodyBuffer.Bytes())))
			message.WriteString("\r\n")
			message.WriteString("\r\n")
		}

		// mail body end
		if textBody != "" && htmlBody != "" {
			message.WriteString("--" + uuid + "--\r\n")
		}

		if _, err = message.WriteTo(wc); err != nil {
			return err
		}

		wc.Close()
	}
	return c.Quit()
}

// 76バイト毎にCRLFを挿入する
func add76crlf(msg string) string {
	var buffer bytes.Buffer
	for k, c := range strings.Split(msg, "") {
		buffer.WriteString(c)
		if k%76 == 75 {
			buffer.WriteString("\r\n")
		}
	}
	return buffer.String()
}

// UTF8文字列を指定文字数で分割
func utf8Split(utf8string string, length int) []string {
	resultString := []string{}
	var buffer bytes.Buffer
	for k, c := range strings.Split(utf8string, "") {
		buffer.WriteString(c)
		if k%length == length-1 {
			resultString = append(resultString, buffer.String())
			buffer.Reset()
		}
	}
	if buffer.Len() > 0 {
		resultString = append(resultString, buffer.String())
	}
	return resultString
}

// サブジェクトをMIMEエンコードする
func encodeSubject(subject string) string {
	var buffer bytes.Buffer
	buffer.WriteString("Subject:")
	for _, line := range utf8Split(subject, 13) {
		buffer.WriteString(" =?utf-8?B?")
		buffer.WriteString(base64.StdEncoding.EncodeToString([]byte(line)))
		buffer.WriteString("?=\r\n")
	}
	return buffer.String()
}
1
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?