I am trying to send an email that contains an attachment (a pdf file) using the amazon SES API.
I am using Symfony2 so I included the AmazonWebServiceBundle in my pr
I managed to create a raw MIME Message in the below manner to send an email with attachment (.pdf file), using Amazon SES sendRawEmail. This is for plain javascript usage; one can surely refine it further add other Content-types too.
Use a library like jsPDF & html2Canvas to create the PDF file & save the contents into a variable & get the base 64 data:
var pdfOutput = pdf.output();
var myBase64Data = btoa(pdfOutput);
Use the below code to create the MIME Message. Note, the sequence is important else the email will end up as a text email exposing all the base 64 data :
var fileName = "Attachment.pdf";
var rawMailBody = "From: testmail@gmail.com\nTo: testmail@gmail.com\n";
rawMailBody = rawMailBody + "Subject: Test Subject\n";
rawMailBody = rawMailBody + "MIME-Version: 1.0\n";
rawMailBody = rawMailBody + "Content-Type: multipart/mixed; boundary=\"NextPart\"\n\n";
rawMailBody = rawMailBody + "--NextPart\n";
rawMailBody = rawMailBody + "Content-Type: application/octet-stream\n";
rawMailBody = rawMailBody + "Content-Transfer-Encoding: base64\n";
rawMailBody = rawMailBody + "Content-Disposition: attachment; filename=\"" + fileName + "\"\n\n";
rawMailBody = rawMailBody + "Content-ID random2384928347923784letters\n";
rawMailBody = rawMailBody + myBase64Data+"\n\n";
rawMailBody = rawMailBody + "--NextPart\n";
Invoke sendRawEmail:
var params = {
RawMessage: {
Data: rawMailBody
},
Destinations: [],
Source: 'testmail@gmail.com'
};
ses.sendRawEmail(params, function(err, data) {
if (err) alert("Error: "+err); // an error occurred
else {
alert("Success: "+data); // successful response
}
});
After many tries, I got to the conclusion that sending emails to Amazon SES directly from the code is too much pain.
So I didn't change a thing in the code and configured my postfix server instead.
I followed this procedure: http://docs.amazonwebservices.com/ses/latest/DeveloperGuide/SMTP.MTAs.Postfix.html and configured integration using STARTTLS.
I had to ask for SMTP credentials in the Amazon console.
Now it's working fine, emails are sent properly through Amazon SES.
Building emails by hand and as you clearly expressed "is too much pain" and so is maintaining your own server just to send emails. That is why I recommend using a library to take care of creating the email message.
For example, Swift mailer has a class called Swift_Message that can be used to create an email message and include attachments.
The following example is taken straight from the documentation:
$message = (new Swift_Message())
->setSubject('Your subject')
->setFrom(['john@doe.com' => 'John Doe'])
->setTo(['receiver@domain.org', 'other@domain.org' => 'A name'])
->setBody('Here is the message itself')
->addPart('<q>Here is the message itself</q>', 'text/html')
->attach(Swift_Attachment::fromPath('my-document.pdf'))
Then just call the toString
method of the message and set it as the RawMessage
$result = $client->sendRawEmail([
'RawMessage' => [
'Data' => $message.toString()
]
]);
Alternatively, you can setup symfony + swift mailer to use SES SMTP endpoint, see the symfony documentation for more information.
Yes, it is a pain to send emails with attachments using SES. Maybe this will help someone else out who is still struggling with it. I've written a simple class that helps simplify the call to sendRawEmail.
Usage:
$subject_str = "Some Subject";
$body_str = "<strong>Some email body</strong>";
$attachment_str = file_get_contents("/htdocs/test/sample.pdf");
//send the email
$result = SESUtils::deliver_mail_with_attachment(
array('email1@gmail.com', 'email2@lutz-engr.com'),
$subject_str, $body_str, 'sender@verifiedbyaws',
$attachment_str);
//now handle the $result if you wish
Class:
<?php
require 'AWSSDKforPHP/aws.phar';
use Aws\Ses\SesClient;
/**
* SESUtils is a tool to make it easier to work with Amazon Simple Email Service
* Features:
* A client to prepare emails for use with sending attachments or not
*
* There is no warranty - use this code at your own risk.
* @author sbossen
* http://right-handed-monkey.blogspot.com
*/
class SESUtils {
const version = "1.0";
const AWS_KEY = "your_aws_key";
const AWS_SEC = "your_aws_secret";
const AWS_REGION = "us-east-1";
const BOUNCER = "bouncer@yourdomain.com"; //if used, this also needs to be a verified email
const LINEBR = "\n";
const MAX_ATTACHMENT_NAME_LEN = 60;
/**
* Usage
* $result = SESUtils::deliver_mail_with_attachment(array('receipt@r.com', 'receipt2@b.com'), $subject_str, $body_str, 'validatedsender@aws', $attachment_str);
* use $result->success to check if it was successful
* use $result->message_id to check later with Amazon for further processing
* use $result->result_text to look for error text if the task was not successful
*
* @param type $to - individual address or array of email addresses
* @param type $subject - UTF-8 text for the subject line
* @param type $body - Text for the email
* @param type $from - email address of the sender (Note: must be validated with AWS as a sender)
* @return \ResultHelper
*/
public static function deliver_mail_with_attachment($to, $subject, $body, $from, &$attachment = "", $attachment_name = "doc.pdf", $attachment_type = "Application/pdf", $is_file = false, $encoding = "base64", $file_arr = null) {
$result = new ResultHelper();
//get the client ready
$client = SesClient::factory(array(
'key' => self::AWS_KEY,
'secret' => self::AWS_SEC,
'region' => self::AWS_REGION
));
//build the message
if (is_array($to)) {
$to_str = rtrim(implode(',', $to), ',');
} else {
$to_str = $to;
}
$msg = "To: $to_str".self::LINEBR;
$msg .="From: $from".self::LINEBR;
//in case you have funny characters in the subject
$subject = mb_encode_mimeheader($subject, 'UTF-8');
$msg .="Subject: $subject".self::LINEBR;
$msg .="MIME-Version: 1.0".self::LINEBR;
$msg .="Content-Type: multipart/alternative;".self::LINEBR;
$boundary = uniqid("_Part_".time(), true); //random unique string
$msg .=" boundary=\"$boundary\"".self::LINEBR;
$msg .=self::LINEBR;
//now the actual message
$msg .="--$boundary".self::LINEBR;
//first, the plain text
$msg .="Content-Type: text/plain; charset=utf-8".self::LINEBR;
$msg .="Content-Transfer-Encoding: 7bit".self::LINEBR;
$msg .=self::LINEBR;
$msg .=strip_tags($body);
$msg .=self::LINEBR;
//now, the html text
$msg .="--$boundary".self::LINEBR;
$msg .="Content-Type: text/html; charset=utf-8".self::LINEBR;
$msg .="Content-Transfer-Encoding: 7bit".self::LINEBR;
$msg .=self::LINEBR;
$msg .=$body;
$msg .=self::LINEBR;
//add attachments
if (!empty($attachment)) {
$msg .="--$boundary".self::LINEBR;
$msg .="Content-Transfer-Encoding: base64".self::LINEBR;
$clean_filename = mb_substr($attachment_name, 0, self::MAX_ATTACHMENT_NAME_LEN);
$msg .="Content-Type: $attachment_type; name=$clean_filename;".self::LINEBR;
$msg .="Content-Disposition: attachment; filename=$clean_filename;".self::LINEBR;
$msg .=self::LINEBR;
$msg .=base64_encode($attachment);
//only put this mark on the last entry
if (!empty($file_arr))
$msg .="==".self::LINEBR;
$msg .="--$boundary";
}
if (!empty($file_arr) && is_array($file_arr)) {
foreach ($file_arr as $file) {
$msg .="Content-Transfer-Encoding: base64".self::LINEBR;
$clean_filename = mb_substr($attachment_name, 0, self::MAX_ATTACHMENT_NAME_LEN);
$msg .="Content-Type: application/octet-stream; name=$clean_filename;".self::LINEBR;
$msg .="Content-Disposition: attachment; filename=$clean_filename;".self::LINEBR;
$msg .=self::LINEBR;
$msg .=base64_encode($attachment);
//only put this mark on the last entry
if (!empty($file_arr))
$msg .="==".self::LINEBR;
$msg .="--$boundary";
}
}
//close email
$msg .="--".self::LINEBR;
//now send the email out
try {
$ses_result = $client->sendRawEmail(array(
'RawMessage' => array('Data' => base64_encode($msg))), array('Source' => $from, 'Destinations' => $to_str));
if ($ses_result) {
$result->message_id = $ses_result->get('MessageId');
} else {
$result->success = false;
$result->result_text = "Amazon SES did not return a MessageId";
}
} catch (Exception $e) {
$result->success = false;
$result->result_text = $e->getMessage()." - To: $to_str, Sender: $from, Subject: $subject";
}
return $result;
}
}
class ResultHelper {
public $success = true;
public $result_text = "";
public $message_id = "";
}
?>
I've written a blog post that addresses this, maybe it will be useful to you or others: http://righthandedmonkey.com/2013/06/how-to-use-amazon-ses-to-send-email-php.html