Email communication is a crucial aspect of many applications, and Amazon Simple Email Service (SES) provides a reliable and scalable solution for sending emails in the Amazon Web Services (AWS) environment. I've created a go package that facilitates sending raw emails with attachments using AWS SES.
Full Code
package oaws
import (
"fmt"
"io/ioutil"
"os"
"github.com/aws/aws-sdk-go/aws/session"
"github.com/rs/zerolog/log"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/ses"
)
func NewSession(region string) (sess *session.Session, err error) {
if region == "" {
log.Fatal().Msg("Missing region in oaws.NewSession")
}
sess, err = session.NewSession(&aws.Config{
Region: aws.String(region)})
return
}
func SendRawEmailWithAttachment(sender, recipient, subject, html, text, fileName, region string, attachmentFile *os.File) error {
sess, err := NewSession(region)
if err != nil {
fmt.Println(err.Error())
return err
}
_, err = attachmentFile.Seek(0, 0)
if err != nil {
fmt.Println("Error seeking to the beginning of the file:", err)
return err
}
attachmentContent, err := ioutil.ReadAll(attachmentFile)
if err != nil {
fmt.Println("Error reading attachment:", err)
return err
}
attachmentData := aws.NewWriteAtBuffer(attachmentContent)
rawEmail := fmt.Sprintf(`From: %s
To: %s
Subject: %s
Content-Type: multipart/mixed;
boundary="a3f166a86b56ff6c37755292d690675717ea3cd9de81228ec2b76ed4a15d6d1a"
--a3f166a86b56ff6c37755292d690675717ea3cd9de81228ec2b76ed4a15d6d1a
Content-Type: multipart/alternative;
boundary="sub_a3f166a86b56ff6c37755292d690675717ea3cd9de81228ec2b76ed4a15d6d1a"
--sub_a3f166a86b56ff6c37755292d690675717ea3cd9de81228ec2b76ed4a15d6d1a
Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: quoted-printable
%s
--sub_a3f166a86b56ff6c37755292d690675717ea3cd9de81228ec2b76ed4a15d6d1a
Content-Type: text/html; charset=iso-8859-1
Content-Transfer-Encoding: quoted-printable
%s
--sub_a3f166a86b56ff6c37755292d690675717ea3cd9de81228ec2b76ed4a15d6d1a--
--a3f166a86b56ff6c37755292d690675717ea3cd9de81228ec2b76ed4a15d6d1a
Content-Type: text/plain; name="%s"
Content-Description: %s
Content-Disposition: attachment;filename="%s";
creation-date="Sat, 05 Aug 2017 19:35:36 GMT";
Content-Transfer-Encoding: base64
%s
--a3f166a86b56ff6c37755292d690675717ea3cd9de81228ec2b76ed4a15d6d1a--
`, sender, recipient, subject, text, html, fileName, fileName, fileName, attachmentData.Bytes())
svc := ses.New(sess)
input := &ses.SendRawEmailInput{
RawMessage: &ses.RawMessage{
Data: []byte(rawEmail),
},
}
_, err = svc.SendRawEmail(input)
if err != nil {
handleSendEmailError(err)
return err
}
return nil
}
func handleSendEmailError(err error) {
if aerr, ok := err.(awserr.Error); ok {
switch aerr.Code() {
case ses.ErrCodeMessageRejected:
fmt.Println(ses.ErrCodeMessageRejected, aerr.Error())
case ses.ErrCodeMailFromDomainNotVerifiedException:
fmt.Println(ses.ErrCodeMailFromDomainNotVerifiedException, aerr.Error())
case ses.ErrCodeConfigurationSetDoesNotExistException:
fmt.Println(ses.ErrCodeConfigurationSetDoesNotExistException, aerr.Error())
default:
fmt.Println(aerr.Error())
}
} else {
fmt.Println(err.Error())
}
}
Sample Usage
func GenerateTXTContent(contentArray []string) string {
var buffer bytes.Buffer
for _, content := range contentArray {
buffer.WriteString(content + "\n")
}
return buffer.String()
}
func GenerateTempFileContent(txtContent, fileName string) (*os.File, error) {
tmpFile, err := os.CreateTemp(".", fileName+"*.txt")
if err != nil {
fmt.Println("Error creating temporary file:", err)
return nil, err
}
defer os.Remove(tmpFile.Name())
_, err = tmpFile.WriteString(txtContent)
if err != nil {
fmt.Println("Error writing to temporary file:", err)
return nil, err
}
return tmpFile, nil
}
var vouchers []string
for i := 0; i < len(generateVoucherResponse.Item); i++ {
vouchers = append(vouchers, generateVoucherResponse.Item[i].Code)
}
txtContent := GenerateTXTContent(vouchers)
file, err := GenerateTempFileContent(txtContent, schoolDetailsRequest.Item.ID.String())
if err != nil {
return utils.ReturnError(w, err, http.StatusInternalServerError)
}
defer file.Close()
recipient := Email
SendRawEmailWithAttachment(consts.SenderEmail, recipient, "Registration Confirmation", `Welcome`, "Welcome", "filename.txt", cfg.AWSSESRegion, file)
Let's break down the key components of this code and understand how it achieves the task of sending emails with attachments.
1. Importing Packages
The code begins by importing necessary packages, including the AWS SDK for Go and other standard Go packages.
package oaws
import (
"fmt"
"io/ioutil"
"os"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/ses"
)
2. Function to Send Raw Email with Attachment
The SendRawEmailWithAttachment function is the core of this package. It takes various parameters such as sender, recipient, subject, HTML and plain text content, file name, AWS region, and an attachment file. It constructs a raw email with a multipart structure that includes both the email body and the attachment.
func SendRawEmailWithAttachment(sender, recipient, subject, html, text, fileName, region string, attachmentFile *os.File) error {
}
3. Creating an AWS Session
The function starts by creating an AWS session using a helper function named NewSession. If an error occurs during session creation, it prints an error message and returns the error.
4. Preparing Attachment Content
The code reads the content of the attachment file and converts it to base64 format. It then constructs the raw email content by combining the email body and attachment.
5. Constructing Raw Email
The raw email is constructed as a multipart message, including both alternative text/plain and text/html content for the email body. It also includes the attachment with relevant metadata.
6. Creating SES Service Client
A new SES service client is created using the AWS session.
svc := ses.New(sess)
The input for sending raw email is created with the raw email data.
input := &ses.SendRawEmailInput{
RawMessage: &ses.RawMessage{
Data: []byte(rawEmail),
},
}
8. Sending Raw Email
Finally, the function attempts to send the raw email using the SES service client. If an error occurs, the handleSendEmailError function is called to handle specific SES-related errors.
9. Handling SES Send Email Errors
A helper function, handleSendEmailError, is defined to handle errors that may occur during the SES send email operation. It checks the error type and prints specific error messages based on the SES error code.
Conclusion
This Go package provides a convenient way to send raw emails with attachments using AWS SES. Just copy what you need, my usage would not be the same as yours. So far as you provide the right paramters to the sendEmail function, you should be good to go.