I am trying to send an email message with both an email body and a file attachment (a CSV file) in Go (Golang).
I am following the mime
standard of a multi-part message, however I am not very familiar with the structure of the messages following that standard. I am vaguely following a Python code snippet from a colleague as a guide which is using the Python library email
(I think this is from the standard library) e.g. MIMEText
and MIMEMultipart
.
The email message body is not showing up when executing the following Go code:
This function should return a byte slice to be used as a parameter for the call to smtp.SendMail
from the Go standard library. See the comments below explaining what's happening to the received email message (the THIS DOES NOT SHOW UP [...]
and the 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
}
Coincidentally I had a similar issue the other day. I needed a blank line between the body content type and the beginning of the body itself. Below is the updated lines for this section of your code:
mime.WriteString("Content-Type: text/plain; charset=utf-8\r\n")
mime.WriteString("\r\nThis should be the email message body (v2)...")
For clarity, this newline (\r\n) doesn't have to be exactly here, it can be appended to the content-type line above. It just needs to see a blank line between the content-type and the beginning of the body.
I'm assuming the attachment is being attached without problems, correct? My assumption is this is because you have the double new-line at the end of the content-disposition line before the attachment data is added.