i.van
i.van

Reputation: 826

AWS SES SDK send email with attachments

I'm using the official AWS Golang SDK to integrate with SES but can't find any information about how to add some attachments (pdf file represented as []byte in code) to the email.

Could you help me?

The current email sending code looks like this:

sesEmailInput := &ses.SendEmailInput{
    Destination: &ses.Destination{
        ToAddresses: []*string{aws.String("To address")},
    },
    Message: &ses.Message{
        Subject: &ses.Content{
            Data: aws.String("Some text"),
        },
        Body: &ses.Body{
            Html: &ses.Content{
                Data: aws.String("Some Text"),
            },
        },
    },
    Source: aws.String("From address"),
    ReplyToAddresses: []*string{
        aws.String("From address"),
    },
}
if _, err := s.sesSession.SendEmail(sesEmailInput); err != nil {
    return err
}

Upvotes: 16

Views: 44001

Answers (3)

Farrukh Malik
Farrukh Malik

Reputation: 796

SES raw messages must be base64-encoded. So, you'll need to get the file content as buffer and encode it as base64 string attachment. Also, you don't need to create a new buffer for raw message data since it already accepts a string data type.

OPTIONAL: You can also omit the Destinations parameter since you're already providing the To field in the raw message data. (You can also provide the Cc and Bcc fields as well)

You could try this for example:

const sendAttachmentEmail = async () => {
  // Yo can use by this way
  const DATA = await workbook.writeToBuffer(); // This workbook is excel generated
  // Or this way
  const DATA = fs.readFileSync("files/demo-invoice.xlsx"); // This is excel file in files folder

  const SOURCE_EMAIL = "[email protected]";
  const TO_EMAIL = "[email protected]";

  let ses_mail = "From: 'AWS SES Attchament Configuration' <" + SOURCE_EMAIL + ">\n";
  ses_mail += "To: " + TO_EMAIL + "\n";
  ses_mail += "Subject: AWS SES Attachment Example\n";
  ses_mail += "MIME-Version: 1.0\n";
  ses_mail += "Content-Type: multipart/mixed; boundary=\"NextPart\"\n\n";
  ses_mail += "--NextPart\n";
  ses_mail += "Content-Type: text/html\n\n";
  ses_mail += "This is the body of the email.\n\n";
  ses_mail += "--NextPart\n";
  ses_mail += "Content-Type: application/octet-stream; name=\"demo-invoice.xlsx\"\n";
  ses_mail += "Content-Transfer-Encoding: base64\n";
  ses_mail += "Content-Disposition: attachment\n\n";
  ses_mail += data.toString("base64").replace(/([^\0]{76})/g, "$1\n") + "\n\n";
  ses_mail += "--NextPart--";

  const params = {
    RawMessage: {
      Data: ses_mail
    },
    Source: "'AWS SES Attchament Configuration' <" + SOURCE_EMAIL + ">'"
  };

  return new Promise((resolve, reject) => {
    ses.sendRawEmail(params, (err) => {
      if (err) {
        return reject(err);
      }
      return resolve();
    });
  });
};

NOTE: The /([^\0]{76})/ regular expression replacement breaks your long lines to make sure that mail servers do not complain about message lines being too long when there is an encoded attachment, which might result in a transient bounce.

Upvotes: 3

Leonardo Filipe
Leonardo Filipe

Reputation: 1762

after several wasted hours, things you should know about raw emails.

follow a structure like this:

boundary=boundary_name \n\n  (create a boundary with boundary= and \n\n)
--boundary_name              (start a boundary with --)
  Header                     (add headers with Content-Type:)
                             (one blank line with \n)
  html or text or file       (add your content)
                             (one blank line with \n)
--boundary_name-- \n\n       (close boundary with -- -- and \n\n)
  

Example with PHP code:

$boundary = md5(time());

$html = "<h1>E-mail Test</h1>";
$html .= "<img src=\"cid:{$attachment['filename']}\">"; // Content-ID

$text = "You need HTML enabled";


$raw = '';
$raw .= "From:{$from}\n";
$raw .= "To:{$to}\n";
$raw .= "Subject:{$subject}\n";
$raw .= "MIME-Version: 1.0\n";
$raw .= "Content-Type: multipart/mixed; boundary=\"{$boundary}\"\n\n";
$raw .= "--{$boundary}\n";

    $raw .= "Content-Type: multipart/alternative; boundary=\"sub_{$boundary}\"\n\n";
    $raw .= "--sub_{$boundary}\n";
        $raw .= "Content-Type: text/plain; charset=\"UTF-8\"\n";
        $raw .= "\n";
        $raw .= "{$text}\n";
        $raw .= "\n";
    $raw .= "--sub_{$boundary}\n";
        $raw .= "Content-Type: text/html; charset=\"UTF-8\"\n";
        $raw .= "Content-Disposition: inline\n";
        $raw .= "\n";
        $raw .= "{$html}\n";
        $raw .= "\n";
    $raw .= "--sub_{$boundary}--\n\n";

foreach ($this->email->getFiles() as $attachment) {
    $raw .= "--{$boundary}\n";
        $raw .= "Content-Type:{$attachment['mimetype']}; name=\"{$attachment['filename']}\"\n"; // name
        $raw .= "Content-Transfer-Encoding:base64\n";
        $raw .= "Content-Disposition:attachment;filename=\"{$attachment['filename']}\"\n"; // filename
        #$raw .= "Content-Disposition:inline;name={$file_name}\n";
        $raw .= "Content-ID:<{$attachment['filename']}>\n";
        $raw .= "\n";
        $raw .= base64_encode($attachment['content']) . "\n";
        $raw .= "\n";
}
$raw .= "--{$boundary}--\n\n";

Final raw Message example:

From:Name From<[email protected]>
To:[email protected],[email protected]
Subject:Your subject
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="b4f53df7c26ae6945f29c42d5c2bb55f"

--b4f53df7c26ae6945f29c42d5c2bb55f
Content-Type: multipart/alternative; boundary="sub_b4f53df7c26ae6945f29c42d5c2bb55f"

--sub_b4f53df7c26ae6945f29c42d5c2bb55f
Content-Type: text/plain; charset="UTF-8"

You need HTML enabled

--sub_b4f53df7c26ae6945f29c42d5c2bb55f
Content-Type: text/html; charset="UTF-8"
Content-Disposition: inline

<h1>E-mail Test</h1>
<img src="cid:qrcode.png"> // <-- from Header Content-ID:<name>

--sub_b4f53df7c26ae6945f29c42d5c2bb55f--

--b4f53df7c26ae6945f29c42d5c2bb55f
Content-Type:image/png; name="qrcode.png"
Content-Transfer-Encoding:base64
Content-Disposition:attachment;filename="qrcode.png"
Content-ID:<qrcode.png> // you can use <img src="cid:qrcode.png"> in html

iVBORw0K== // base64encoded file contents

--b4f53df7c26ae6945f29c42d5c2bb55f
Content-Type:image/png; name="another_file.png"
Content-Transfer-Encoding:base64
Content-Disposition:attachment;filename="another_file.png"
Content-ID:<another_file.png>

iVBORw0KGg== // base64encoded file contents

--b4f53df7c26ae6945f29c42d5c2bb55f--

Don't forget, after Headers, one blank lines before and after content

For PHP AWS SDK

try {
    $client = new SesClient($options);
    $result = $client->sendRawEmail([
        'RawMessage' => [
            'Data' => $raw
        ],
    ]);
    echo $result['MessageId'];
}
catch (AwsException $e) {
    echo $e->getAwsErrorMessage();       
    return false;
}

Upvotes: 3

Anthony Neace
Anthony Neace

Reputation: 26031

To send attachments, use the SendRawEmail API instead of SendEmail. AWS documentation will generally refer to this as constructing a 'raw message' instead of explicitly calling out how to send attachments.

Example

From the AWS SDK for Go API Reference, linked below:

params := &ses.SendRawEmailInput{
    RawMessage: &ses.RawMessage{ // Required
        Data: []byte("PAYLOAD"), // Required
    },
    ConfigurationSetName: aws.String("ConfigurationSetName"),
    Destinations: []*string{
        aws.String("Address"), // Required
        // More values...
    },
    FromArn:       aws.String("AmazonResourceName"),
    ReturnPathArn: aws.String("AmazonResourceName"),
    Source:        aws.String("Address"),
    SourceArn:     aws.String("AmazonResourceName"),
    Tags: []*ses.MessageTag{
        { // Required
            Name:  aws.String("MessageTagName"),  // Required
            Value: aws.String("MessageTagValue"), // Required
        },
        // More values...
    },
}
resp, err := svc.SendRawEmail(params)

Further Reading

Upvotes: 19

Related Questions