简体   繁体   中英

how to send attachments in emails using AWS SES

I'm trying to send an email programmatically using amazon's SES library using the code from here . After tweaking I have the following pieces of code.

SESUtils.php

<?php

require_once('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
 *
 * Update: Error checking and new params input array provided by Michael Deal
 */
class SESUtils {

const version = "1.0";
const AWS_KEY = SES_KEY;
const AWS_SEC = SES_SECRET;
const AWS_REGION = "us-east-1";
const MAX_ATTACHMENT_NAME_LEN = 60;

/**
 * Usage:
    $params = array(
      "to" => "email1@gmail.com",
      "subject" => "Some subject",
      "message" => "<strong>Some email body</strong>",
      "from" => "sender@verifiedbyaws",
      //OPTIONAL
      "replyTo" => "reply_to@gmail.com",
      //OPTIONAL
      "files" => array(
        1 => array(
           "name" => "filename1", 
          "filepath" => "/path/to/file1.txt", 
          "mime" => "application/octet-stream"
        ),
        2 => array(
           "name" => "filename2", 
          "filepath" => "/path/to/file2.txt", 
          "mime" => "application/octet-stream"
        ),
      )
    );

  $res = SESUtils::sendMail($params);

 * NOTE: When sending a single file, omit the key (ie. the '1 =>') 
 * or use 0 => array(...) - otherwise the file will come out garbled
 * 
 * use $res->success to check if it was successful
 * use $res->message_id to check later with Amazon for further processing
 * use $res->result_text to look for error text if the task was not successful
 * 
 * @param array $params - array of parameters for the email
 * @return \ResultHelper
 */
public static function sendMail($params) {

    $to = self::getParam($params, 'to', true);
    $subject = self::getParam($params, 'subject', true);
    $body = self::getParam($params, 'message', true);
    $from = self::getParam($params, 'from', true);
    $replyTo = self::getParam($params, 'replyTo');
    $files = self::getParam($params, 'files');

    $res = 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\n";
    $msg .= "From: $from\n";

    if ($replyTo) {
        $msg .= "Reply-To: $replyTo\n";
    }

    // in case you have funny characters in the subject
    $subject = mb_encode_mimeheader($subject, 'UTF-8');
    $msg .= "Subject: $subject\n";
    $msg .= "MIME-Version: 1.0\n";
    $msg .= "Content-Type: multipart/alternative;\n";
    $boundary = uniqid("_Part_".time(), true); //random unique string
    $msg .= " boundary=\"$boundary\"\n";
    $msg .= "\n";

    // now the actual message
    $msg .= "--$boundary\n";

    // first, the plain text
    $msg .= "Content-Type: text/plain; charset=utf-8\n";
    $msg .= "Content-Transfer-Encoding: 7bit\n";
    $msg .= "\n";
    $msg .= strip_tags($body);
    $msg .= "\n";

    // now, the html text
    $msg .= "--$boundary\n";
    $msg .= "Content-Type: text/html; charset=utf-8\n";
    $msg .= "Content-Transfer-Encoding: 7bit\n";
    $msg .= "\n";
    $msg .= $body;
    $msg .= "\n";

    // add attachments
    if (is_array($files)) {
        $count = count($files);
        foreach ($files as $idx => $file) {
            if ($idx !== 0)
                $msg .= "\n";
            $msg .= "--$boundary\n";
            $msg .= "Content-Transfer-Encoding: base64\n";
            $clean_filename = mb_substr($file["name"], 0, self::MAX_ATTACHMENT_NAME_LEN);
            $msg .= "Content-Type: {$file['mime']}; name=$clean_filename;\n";
            $msg .= "Content-Disposition: attachment; filename=$clean_filename;\n";
            $msg .= "\n";
            $msg .= base64_encode(file_get_contents($file['filepath']));
            if (($idx + 1) === $count)
                $msg .= "==\n";
            $msg .= "--$boundary";
        }
        // close email
        $msg .= "--\n";
    }

    // now send the email out
    try {
        file_put_contents("log.txt", $msg);
        $ses_result = $client->sendRawEmail(
                array(
            'RawMessage' => array(
                'Data' => base64_encode($msg)
            )
                ), array(
            'Source' => $from,
            'Destinations' => $to_str
                )
        );
        if ($ses_result) {
            $res->message_id = $ses_result->get('MessageId');
        } else {
            $res->success = false;
            $res->result_text = "Amazon SES did not return a MessageId";
        }
    } catch (Exception $e) {
        $res->success = false;
        $res->result_text = $e->getMessage().
                " - To: $to_str, Sender: $from, Subject: $subject";
    }
    return $res;
}

private static function getParam($params, $param, $required = false) {
    $value = isset($params[$param]) ? $params[$param] : null;
    if ($required && empty($value)) {
        throw new Exception('"'.$param.'" parameter is required.');
    } else {
        return $value;
    }
}

}

class ResultHelper {

    public $success = true;
    public $result_text = "";
    public $message_id = "";

}

?>

And the function I'm using to send the actual email

function sendAttachmentEmail($from, $to, $subject, $message, $attachmentPaths=array()){
    client = SesClient::factor(array('key' => SES_KEY, 'secret' => SES_SECRET, 'region' => 'us-east-1'));
    $attachments = array();
    foreach($attachmentPaths as $path){
        $fileName = explode("/",, $path);
        $fileName = $fileName[count($fileName)-1];
        $extension = explode(".", $fileName);
        $extension = strtoupper($extension[count($extension)-1]);
        $mimeType = "";
        if($extension == 'PDF') $mimeType = 'application/pdf';
        elseif($extension == 'CSV') $mimeType = 'test/csv';
        elseif($extension == 'XLS') $mimeType = 'application/vnd.ms-excel';
        array_push($attachments, array("name" => $fileName, "filepath" => $path, "mime" => $mimeType));
    }
    $params = array(
        "from" => $from,
        "to" => $to,
        "subject" => $subject,
        "message" => $message,
        "replyTo" => $from,
        "files" => $attachments
    );
    $res = SESUtils::sendMail($params);
    return $res;
}

sendAttachmentEmail("jesse@aol.com", "jesse@aol.com", 'test', 'test', array("/path/to/file.pdf"));

When I run this the message returned is an error saying "Expected ';', got "Reports" - To: jesse@aol.com, Sender: jesse@aol.com, Subject: test" . Anyone know what I might be missing? The contents of the msg being sent is

To: jesse@aol.com
From: jesse@aol.com
Reply-To: jesse@aol.com
Subject: test
MIME-Version: 1.0
Content-Type: multipart/alternative;
 boundary="_Part_142255491754ca7725b0bf89.40746157"

--_Part_142255491754ca7725b0bf89.40746157
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: 7bit

test
--_Part_142255491754ca7725b0bf89.40746157
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: 7bit

test
--_Part_142255491754ca7725b0bf89.40746157
Content-Transfer-Encoding: base64
Content-Type: application/pdf; name=file.pdf;
Content-Disposition: attachment; filename=file.pdf;

Here is my working solution for PHP 7 using V3.x of AWS PHP SDK. This solution will send a HTML mail with one attachment:

use Aws\Ses\SesClient;

$gAWS_SES_client = SesClient::factory(array(
    'version'=> 'latest',
    'region' => 'eu-west-1',
    'credentials' => array(
        'key' => 'my_secret_key',
        'secret'  => 'my_secret_pw'
  ) 
));



function sendRAWEmailViaSES($myArrayToAdresses, $myFromAdress, $mySubject, $myHTMLBody, $myFilePath){
    global $gAWS_SES_client;

    echo "\n<BR>Now in sendRAWEmailViaSES()<BR>\n";

    $myStringToAddresses = implode(",", $myArrayToAdresses);

    $myFileName = basename($myFilePath);;

    $myDataAttachedFile = file_get_contents($myFilePath);
    $myDataAttachedFile = chunk_split(base64_encode($myDataAttachedFile));  

    $myFileMimeInfo = finfo_open(FILEINFO_MIME_TYPE);
    $myFileMimeType = finfo_file($myFileMimeInfo, $myFilePath);     

    $mySeparator = md5(time());
    $mySeparator_multipart = md5($mySubject . time());      

    $myMessage = "";

    $myMessage .= "MIME-Version: 1.0\n";

    $myMessage .= "To: ".$myStringToAddresses."\n"; 
    $myMessage .= "From:".$myFromAdress."\n";   
    $myMessage .= "Subject:".$mySubject."\n";

    $myMessage .= "Content-Type: multipart/mixed; boundary=\"".$mySeparator_multipart."\"\n";
    $myMessage .= "\n--".$mySeparator_multipart."\n";

    $myMessage .= "Content-Type: multipart/alternative; boundary=\"".$mySeparator."\"\n";
    $myMessage .= "\n--".$mySeparator."\n";

    $myMessage .= "Content-Type: text/html; charset=\"UTF-8\"\n";
    $myMessage .= "\n".$myHTMLBody."\n";
    $myMessage .= "\n--".$mySeparator."--\n";

    $myMessage .= "--".$mySeparator_multipart."\n";
    $myMessage .= "Content-Type: ".$myFileMimeType."; name=\"".$myFileName."\"\n";
    $myMessage .= "Content-Disposition: attachment; filename=\"".$myFileName."\"\n";
    $myMessage .= "Content-Transfer-Encoding: base64\n\n";
    $myMessage .= $myDataAttachedFile."\n";
    $myMessage .= "--".$mySeparator_multipart."--";

    //echo "\n<BR>Message:<BR>\n";
    //print_r($myMessage);
    //echo "<BR>\n";        


    $myArraySES = [
        'Source'       => $myFromAdress,
        'Destinations' => $myArrayToAdresses,
        'RawMessage'   => [
            'Data' => $myMessage
        ]
    ];  

    echo "\n<HR>Trying to send mail via AAWS SES <BR>\n";


    try{
        $myResult = true;
        $myAPIResult = $gAWS_SES_client->sendRawEmail($myArraySES);

        //save the MessageId which can be used to track the request
        //$myMessageArrayID = $myAPIResult->get('MessageId');
        //echo("MessageId: $msg_id");

        //view sample output
        echo "\n<BR>SES Result:<BR>\n";
        print_r($myAPIResult);
        echo "<BR>\n";

    } catch (Exception $myObjError) {
        //An error happened and the email did not get sent
        $myErrorInfo = $myObjError->getMessage();

        echo "\n<BR>*** SES ERROR:<BR>\n";
        print_r($myErrorInfo);
        echo "<BR>\n";      

        $myfile = fopen("ses_send_error.txt", "w");
        fwrite($myfile, $myErrorInfo);
        fclose($myfile);

    }   


}

Late but i hope this could help others.

The best way to achieve this goal is using PHPMailer to compose the email and get the MIMEMessage to pass it as RawMessage to AWS SES client .


Installation with composer:

$ composer require phpmailer/phpmailer

PHPMailer naive example:

// Create PHPMailer instance
$mail = new \PHPMailer\PHPMailer\PHPMailer;

// Set the email values
$mail->setFrom("foo@bar.com", "Jhon Doe");
$mail->addAddress("destination@somedomain.com");
$mail->Subject = "Hi There ! ";
$mail->Body = <<<EOS
Your email content, <br>
you can write it even as a <strong>HTML document</strong>
EOS;
$mail->AltBody = <<<EOS
Your email content, here you write plain text body for clients that do not support html.
EOS;

// Here you attach your file or files
// @string path
// @string file_name. If not specified, it takes the original name from the path.
$mail->addAttachment("/path/to/file.xlsx", 'my_file.xlsx');

// Here is the magic
$mail->preSend();
$rawMessage = $mail->getSentMIMEMessage();

Now, you can pass the MIMEMessage to the sendRawEmail method in the SES SDK

$credentials = new Aws\Credentials\Credentials('XXXXX', 'YYYYYYYYYYY');
$ses_client = new Aws\Ses\SesClient([
        'credentials' => $credentials,
        'version' => '2010-12-01',
        'region'  => 'us-west-2'
]);


$ses_client->sendRawEmail([
            "RawMessage" => [
                "Data" => $rawMessage
            ]
]);

That's it !

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM