Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Golang - send an email with attachments.
package main
import (
"bytes"
"encoding/base64"
"fmt"
"io/ioutil"
"mime/multipart"
"net/smtp"
"os"
"path/filepath"
)
var (
host = os.Getenv("EMAIL_HOST")
username = os.Getenv("EMAiL_USERNAME")
password = os.Getenv("EMAIL_PASSWORD")
portNumber = os.Getenv("EMAIL_PORT")
)
type Sender struct {
auth smtp.Auth
}
type Message struct {
To []string
CC []string
BCC []string
Subject string
Body string
Attachments map[string][]byte
}
func New() *Sender {
auth := smtp.PlainAuth("", username, password, host)
return &Sender{auth}
}
func (s *Sender) Send(m *Message) error {
return smtp.SendMail(fmt.Sprintf("%s:%s", host, portNumber), s.auth, username, m.To, m.ToBytes())
}
func NewMessage(s, b string) *Message {
return &Message{Subject: s, Body: b, Attachments: make(map[string][]byte)}
}
func (m *Message) AttachFile(src string) error {
b, err := ioutil.ReadFile(src)
if err != nil {
return err
}
_, fileName := filepath.Split(src)
m.Attachments[fileName] = b
return nil
}
func (m *Message) ToBytes() []byte {
buf := bytes.NewBuffer(nil)
withAttachments := len(m.Attachments) > 0
buf.WriteString(fmt.Sprintf("Subject: %s\n", m.Subject))
buf.WriteString(fmt.Sprintf("To: %s\n", strings.Join(m.To, ",")))
if len(m.CC) > 0 {
buf.WriteString(fmt.Sprintf("Cc: %s\n", strings.Join(m.CC, ",")))
}
if len(m.BCC) > 0 {
buf.WriteString(fmt.Sprintf("Bcc: %s\n", strings.Join(m.BCC, ",")))
}
buf.WriteString("MIME-Version: 1.0\n")
writer := multipart.NewWriter(buf)
boundary := writer.Boundary()
if withAttachments {
buf.WriteString(fmt.Sprintf("Content-Type: multipart/mixed; boundary=%s\n", boundary))
buf.WriteString(fmt.Sprintf("--%s\n", boundary))
} else {
buf.WriteString("Content-Type: text/plain; charset=utf-8\n")
}
buf.WriteString(m.Body)
if withAttachments {
for k, v := range m.Attachments {
buf.WriteString(fmt.Sprintf("\n\n--%s\n", boundary))
buf.WriteString(fmt.Sprintf("Content-Type: %s\n", http.DetectContentType(v)))
buf.WriteString("Content-Transfer-Encoding: base64\n")
buf.WriteString(fmt.Sprintf("Content-Disposition: attachment; filename=%s\n", k))
b := make([]byte, base64.StdEncoding.EncodedLen(len(v)))
base64.StdEncoding.Encode(b, v)
buf.Write(b)
buf.WriteString(fmt.Sprintf("\n--%s", boundary))
}
buf.WriteString("--")
}
return buf.Bytes()
}
func main() {
sender := New()
m := NewMessage("Test", "Body message.")
m.To = []string{"to@gmail.com"}
m.CC = []string{"copy1@gmail.com", "copy2@gmail.com"}
m.BCC = []string{"bc@gmail.com"}
m.AttachFile("/path/to/file")
fmt.Println(sender.Send(m))
}
@AddyM
Copy link

AddyM commented Mar 3, 2021

Hi ,

With adding a CSV file as an attachment , it seems we need to change the MIME type?
As i am getting the empty csv file with the email sent with above code

even with below change getting empty csv file as an attachment
buf.WriteString(fmt.Sprintf("\n\n--%s\n", boundary))
>>buf.WriteString("Content-Type: text/csv\n")
buf.WriteString("Content-Transfer-Encoding: base64\n")
buf.WriteString(fmt.Sprintf("Content-Disposition: attachment; filename=%s\n", k))

@douglasmakey
Copy link
Author

douglasmakey commented Mar 3, 2021

Hi @AddyM I made a small change that should help you with that, I tested it and it worked, also you can contact me directly, I will gladly give you a hand.

https://twitter.com/douglasmakey
douglasmakey@gmail.com

@kuncolaxmono
Copy link

kuncolaxmono commented Aug 6, 2021

Send pdf to yopmail.. can you help me, for this problem.. thanks..

Screen Shot 2021-08-06 at 11 42 27

@ryanprtma
Copy link

ryanprtma commented Mar 9, 2022

i tried this with mailtrap as :

Screenshot from 2022-03-09 07-28-15

but i got this

Screenshot from 2022-03-09 07-28-57

@stvoidit
Copy link

stvoidit commented Mar 9, 2022

i tried this with mailtrap as :

Screenshot from 2022-03-09 07-28-15

but i got this

Screenshot from 2022-03-09 07-28-57

https://github.com/stvoidit/gosmtp

Please try my version based on the example of this code. Due to the specific situation, I have to use my own implementation, because popular solutions for some reason do not work, although I have implemented very basic things and perhaps not in the best way, but it works for me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment