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))
}
@stvoidit
Copy link

stvoidit commented Dec 17, 2019

Thanks for your code. A few days spent looking for a good solution, there were problems with sending files and my mail service (yandex.ru). slightly redesigned your code for yourself, in the standard version does not work on SSL.
You can see my fork if you interesting. Thx

@hasanAjsf
Copy link

hasanAjsf commented Oct 22, 2020

I tried with with O365 email as:

var (
	host       = "smtp.office365.com"        // os.Getenv("EMAIL_HOST")
	username   = "xxxx" // os.Getenv("EMAiL_USERNAME")
	password   = "xxxx"                 // os.Getenv("EMAIL_PASSWORD")
	portNumber = 587                         // os.Getenv("EMAIL_PORT")
)

But got the below error:

dial tcp: lookup tcp/%!s(int=587): getaddrinfow: The specified class was not found.

The I fixed the port to be:

portNumber = "587" 

but I got:

504 5.7.4 Unrecognized authentication type [GV0P278CA0003.CHEP278.PROD.OUTLOOK.COM]

Any thought?

@rishab-zomato
Copy link

rishab-zomato commented Dec 19, 2020

How to add array of bcc,cc to buf?

@afrizuko
Copy link

afrizuko commented Feb 25, 2021

How about simply adding headers for Bcc and Cc just like Subject like below.

buf.WriteString(fmt.Sprintf("Cc: %s\n", m.Ccied)) //Cc
buf.WriteString(fmt.Sprintf("Bcc: %s\n", m.Bccied)) //Bcc

@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