title: how to send attachments in emails using AWS SES
body: <p>I'm trying to send an email programmatically using amazon's SES library using the code from <a href="http://www.righthandedmonkey.com/2013/06/how-to-use-amazon-ses-to-send-email-php.html" rel="nofollow">here</a>. After tweaking I have the following pieces of code.</p>
  
  <p>SESUtils.php </p>
  
  <pre><code>&lt;?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" =&gt; "email1@gmail.com",
        "subject" =&gt; "Some subject",
        "message" =&gt; "&lt;strong&gt;Some email body&lt;/strong&gt;",
        "from" =&gt; "sender@verifiedbyaws",
        //OPTIONAL
        "replyTo" =&gt; "reply_to@gmail.com",
        //OPTIONAL
        "files" =&gt; array(
          1 =&gt; array(
             "name" =&gt; "filename1", 
            "filepath" =&gt; "/path/to/file1.txt", 
            "mime" =&gt; "application/octet-stream"
          ),
          2 =&gt; array(
             "name" =&gt; "filename2", 
            "filepath" =&gt; "/path/to/file2.txt", 
            "mime" =&gt; "application/octet-stream"
          ),
        )
      );
  
    $res = SESUtils::sendMail($params);
  
   * NOTE: When sending a single file, omit the key (ie. the '1 =&gt;') 
   * or use 0 =&gt; array(...) - otherwise the file will come out garbled
   * 
   * use $res-&gt;success to check if it was successful
   * use $res-&gt;message_id to check later with Amazon for further processing
   * use $res-&gt;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' =&gt; self::AWS_KEY,
          'secret' =&gt; self::AWS_SEC,
          'region' =&gt; 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 =&gt; $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-&gt;sendRawEmail(
                  array(
              'RawMessage' =&gt; array(
                  'Data' =&gt; base64_encode($msg)
              )
                  ), array(
              'Source' =&gt; $from,
              'Destinations' =&gt; $to_str
                  )
          );
          if ($ses_result) {
              $res-&gt;message_id = $ses_result-&gt;get('MessageId');
          } else {
              $res-&gt;success = false;
              $res-&gt;result_text = "Amazon SES did not return a MessageId";
          }
      } catch (Exception $e) {
          $res-&gt;success = false;
          $res-&gt;result_text = $e-&gt;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 &amp;&amp; empty($value)) {
          throw new Exception('"'.$param.'" parameter is required.');
      } else {
          return $value;
      }
  }
  
  }
  
  class ResultHelper {
  
      public $success = true;
      public $result_text = "";
      public $message_id = "";
  
  }
  
  ?&gt;
  </code></pre>
  
  <p>And the function I'm using to send the actual email</p>
  
  <pre><code>function sendAttachmentEmail($from, $to, $subject, $message, $attachmentPaths=array()){
      client = SesClient::factor(array('key' =&gt; SES_KEY, 'secret' =&gt; SES_SECRET, 'region' =&gt; '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" =&gt; $fileName, "filepath" =&gt; $path, "mime" =&gt; $mimeType));
      }
      $params = array(
          "from" =&gt; $from,
          "to" =&gt; $to,
          "subject" =&gt; $subject,
          "message" =&gt; $message,
          "replyTo" =&gt; $from,
          "files" =&gt; $attachments
      );
      $res = SESUtils::sendMail($params);
      return $res;
  }
  
  sendAttachmentEmail("jesse@aol.com", "jesse@aol.com", 'test', 'test', array("/path/to/file.pdf"));
  </code></pre>
  
  <p>When I run this the message returned is an error saying <code>"Expected ';', got "Reports" - To: jesse@aol.com, Sender: jesse@aol.com, Subject: test"</code>. Anyone know what I might be missing? The contents of the msg being sent is</p>
  
  <pre><code>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;
  </code></pre>