LoginSignup
1
3

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