I'm creating an email client using Golang, to send email with a CSV file attached. Everything is working fine except that in the received email attachment, I can see some unwanted extra characters at the end of the file.
My code snippet:
import (
"bytes"
"encoding/base64"
"fmt"
"mime/multipart"
"net/smtp"
...
)
func SendEmail(cfg Config) error {
body := bytes.NewBuffer(nil)
body.WriteString(fmt.Sprintf("From: %s\n", cfg.EmailFrom))
body.WriteString(fmt.Sprintf("To: %s\n", cfg.EmailTo))
body.WriteString(fmt.Sprintf("Subject: %s\n", cfg.EmailSubject))
// csv file to attach
fileContents := `column1,column2,column3\nAAA,BBB,CCC\nDDD,EEE,FFF\n`
fileContentBytes := []byte(fileContents)
body.WriteString("MIME-Version: 1.0\n")
writer := multipart.NewWriter(body)
boundary := writer.Boundary()
// attach file
body.WriteString("Content-Type: text/plain\n")
body.WriteString("Content-Transfer-Encoding: base64\n")
body.WriteString(fmt.Sprintf("Content-Disposition: attachment; filename=%s\n", "test-filename"))
encodedBytes := make([]byte, base64.StdEncoding.EncodedLen(len(fileContentBytes)))
base64.StdEncoding.Encode(encodedBytes, fileContentBytes)
body.Write(encodedBytes)
body.WriteString(fmt.Sprintf("\n--%s--", boundary))
err = smtp.SendMail(cfg.EmailSMTPHost+":"+cfg.EmailSMTPPort,
nil, cfg.EmailFrom, []string{cfg.EmailTo}, body.Bytes())
if err != nil {
return errors.Wrap(err, "smtp.SendMail failed")
}
return nil
}
Expected csv file:
column1,column2,column3
AAA,BBB,CCC
DDD,EEE,FFF
Obtained csv file:
column1,column2,column3
AAA,BBB,CCC
DDD,EEE,FFF
5k§xõí»ã}8
Anything wrong in the file contents encoding? Any help will be appreciated, thanks!
This code has at least two problems: missing empty line to separate MIME header and body and then adding some MIME boundary at the end even though this is no multipart mail. Currently the created mail looks like this:
From: me@example.com
To: you@example.com
Subject: test
MIME-Version: 1.0
Content-Type: text/plain
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=test-filename
Y29sdW1uMSxjb2x1bW4yLGNvbHVtbjNcbkFBQSxCQkIsQ0NDXG5EREQsRUVFLEZGRlxu
--973d0754ef322150f1977af176c9e1917c6dea9dfa0390e8e99af038c086--
The wrong boundary at the end gets decoded as base64 with invalid base64 characters like "-" simply being ignored. This causes the garbage at the end of the output.
It should instead look like this as a single part. Note the missing (wrong) end-boundary and note the empty line between MIME header and body.
From: me@example.com
To: you@example.com
Subject: test
MIME-Version: 1.0
Content-Type: text/plain
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=test-filename
Y29sdW1uMSxjb2x1bW4yLGNvbHVtbjNcbkFBQSxCQkIsQ0NDXG5EREQsRUVFLEZGRlxu
Alternatively it should be done as a multipart mail as shown below. Note the different Content-Type in the main MIME header.
From: me@example.com
To: you@example.com
Subject: test
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary=973d0754ef322150f1977af176c9e1917c6dea9dfa0390e8e99af038c086
--973d0754ef322150f1977af176c9e1917c6dea9dfa0390e8e99af038c086
Content-Type: text/plain
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=test-filename
Y29sdW1uMSxjb2x1bW4yLGNvbHVtbjNcbkFBQSxCQkIsQ0NDXG5EREQsRUVFLEZGRlxu
--973d0754ef322150f1977af176c9e1917c6dea9dfa0390e8e99af038c086--