在 Golang 中附加文件并通过 SMTP 发送时没有正文部分的电子邮件

Email message with no body part when attaching a file and sending via SMTP in Golang

我正在尝试在 Go (Golang) 中发送包含电子邮件正文和文件附件(CSV 文件)的电子邮件。

我遵循 mime 多部分消息标准,但是我不太熟悉遵循该标准的消息结构。我模糊地遵循一位同事的 Python 代码片段作为使用 Python 库 email 的指南(我认为这是来自标准库)例如MIMETextMIMEMultipart.

执行以下 Go 代码时,电子邮件正文未显示

此函数应该 return 一个字节片,用作从 Go 标准库调用 smtp.SendMail 的参数。请参阅下面的评论,解释收到的电子邮件发生了什么(THIS DOES NOT SHOW UP [...]THIS ALSO DOES NOT SHOW UP [...])。

func msgWithAttachment(subject, filePath string) ([]byte, error) {
    // this is the separator used for the various parts of the MIME message structure
    // identified as "boundary"
    bPlaceholder := "our-custom-separator"

    // the message setup of the common/standard initial part
    mime := bytes.NewBuffer(nil)
    mime.WriteString(fmt.Sprintf("Subject: %s\r\nMIME-Version: 1.0\r\n", subject))
    mime.WriteString(fmt.Sprintf("Content-Type: multipart/mixed; boundary=%s\r\n", bPlaceholder))

    // THIS DOES NOT SHOW UP AS THE BODY OF THE EMAIL...
    // mime.WriteString("\r\n")
    // mime.WriteString(fmt.Sprintf("--%s\r\n", bPlaceholder))
    // mime.WriteString("This should be the email message body (v1)...")
    // mime.WriteString("\r\n")

    // THIS ALSO DOES NOT SHOW UP AS THE BODY OF THE EMAIL...
    // BUT IS NEEDED OTHERWISE THE EMAIL MESSAGE SEEMS TO CONTAIN AS ATTACHMENT THE EMAIL MESSAGE ITSELF
    // (CONTAINING ITSELF THE REAL ATTACHMENT)
    mime.WriteString(fmt.Sprintf("--%s\r\n", bPlaceholder))
    mime.WriteString("Content-Type: text/plain; charset=utf-8\r\n")
    mime.WriteString("This should be the email message body (v2)...")

    // attach a file from the filesystem
    _, msgFilename := filepath.Split(filePath)
    mime.WriteString(fmt.Sprintf("\n--%s\r\n", bPlaceholder))
    mime.WriteString("Content-Type: application/octet-stream\r\n")
    mime.WriteString("Content-Description: " + msgFilename + "\r\n")
    mime.WriteString("Content-Transfer-Encoding: base64\r\n")
    mime.WriteString("Content-Disposition: attachment; filename=\"" + msgFilename + "\"\r\n\r\n")
    fileContent, err := ioutil.ReadFile(filePath) // read and encode the content of the file
    if err != nil {
        return nil, err
    }
    b := make([]byte, base64.StdEncoding.EncodedLen(len(fileContent)))
    base64.StdEncoding.Encode(b, fileContent)
    mime.Write(b)

    // footer of the email message
    mime.WriteString("\r\n--" + bPlaceholder + "--\r\n\r\n")

    return mime.Bytes(), nil
}

巧合的是,前几天我遇到了类似的问题。我需要在正文内容类型和正文本身的开头之间有一个空行。以下是这部分代码的更新行:

    mime.WriteString("Content-Type: text/plain; charset=utf-8\r\n")
    mime.WriteString("\r\nThis should be the email message body (v2)...")

为清楚起见,这个换行符 (\r\n) 不必完全在此处,它可以附加到上面的内容类型行。它只需要在内容类型和正文开头之间看到一个空行。

我假设附件安装没有问题,对吗?我的假设是,这是因为在添加附件数据之前,内容配置行末尾有双换行符。

阅读 RFC 规范对我有帮助:https://www.w3.org/Protocols/rfc1341/7_2_Multipart.html

Note that the encapsulation boundary must occur at the beginning of a line, i.e., following a CRLF, and that that initial CRLF is considered to be part of the encapsulation boundary rather than part of the preceding part. The boundary must be followed immediately either by another CRLF and the header fields for the next part, or by two CRLFs, in which case there are no header fields for the next part (and it is therefore assumed to be of Content-Type text/plain).