Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I send an email with attachments from a PHP form?

How can I send an email with attachments from a PHP form?

like image 678
Nathan Long Avatar asked Aug 25 '09 20:08

Nathan Long


People also ask

Can PHP be used to send emails?

PHP makes use of mail() function to send an email. This function requires three mandatory arguments that specify the recipient's email address, the subject of the the message and the actual message additionally there are other two optional parameters.

How do you send an attachment in HTML email?

Have the user choose a file. Upload the file to a server. Have the server return a random file name after upload. Build a mailto: link that contains the URL to the uploaded file in the message body.


1 Answers

It's probably best to use an existing tool, as others have suggested in their answers. However, if you want to roll your own or just understand how, keep reading.

The HTML

There are really just two requirements in your HTML for sending file attachments.

  • Your form needs to have this attribute: enctype="multipart/form-data"
  • You need at least one field like <input type="file" name="examplefile">. This allows the user to browse for a file to attach.

If you have both of these, the browser will upload any attached files along with the form submission.

Side note: These are saved as temporary files on the server. For this example, we'll take their data and email it, but if you move the temporary files to a permanent location, you've just created a file upload form.

The MIME Email Format

This tutorial is great for understanding how to build up a MIME email (which can contain HTML content, a plain text version, attachments, etc) in PHP. I used it as a starting point.

Basically, you do three things:

  • Declare up front that this email will have multiple types of content in it
  • Declare a string of text that you will use to separate the different sections
  • Define each section and stick in the appropriate content. In the case of file attachments, you have to specify the type and encode them in ASCII.
    • Each section will have a content-type such as image/jpg or application/pdf. More info can be found here. (My example script pulls this information from each file using built-in PHP functions.)

The PHP

Once the form is submitted, any files uploaded by the browser (see the HTML section) will be available via the $_FILES variable, which contains 'An associative array of items uploaded to the current script via the HTTP POST method.'

The documentation on $_FILES is lousy, but after an upload, you can run print_r($_FILES) to see how it works. It will output something like this:

Array ( [examplefile] => Array ( [name] => your_filename.txt 
[type] => text/plain [tmp_name] => 
C:\path\to\tmp\file\something.tmp [error] => 0 [size] => 200 ) ) 

You can then get the data in the associated temp file by using file_get_contents($_FILES['examplefile']['tmp_name']).

Side note on file size limits

php.ini has some settings that limit attachment size. See this discussion for more info.

An Example PHP Function

I created the following function, which can be included on the page and used to gather up any file attachments submitted with a form. Feel free to use it and/or adapt it for your needs.

The total attachment limit is arbitrary, but large amounts may bog down the mail() script or be rejected by a sending or receiving email server. Do your own testing.

(Note: The mail() function in PHP depends on information in php.ini to know how to send your email.)

function sendWithAttachments($to, $subject, $htmlMessage){
  $maxTotalAttachments=2097152; //Maximum of 2 MB total attachments, in bytes
  $boundary_text = "anyRandomStringOfCharactersThatIsUnlikelyToAppearInEmail";
  $boundary = "--".$boundary_text."\r\n";
  $boundary_last = "--".$boundary_text."--\r\n";

  //Build up the list of attachments, 
  //getting a total size and adding boundaries as needed
  $emailAttachments = "";
  $totalAttachmentSize = 0;
  foreach ($_FILES as $file) {
    //In case some file inputs are left blank - ignore them
    if ($file['error'] == 0 && $file['size'] > 0){
      $fileContents = file_get_contents($file['tmp_name']);
      $totalAttachmentSize += $file['size']; //size in bytes
      $emailAttachments .= "Content-Type: " 
      .$file['type'] . "; name=\"" . basename($file['name']) . "\"\r\n"
      ."Content-Transfer-Encoding: base64\r\n"
      ."Content-disposition: attachment; filename=\"" 
      .basename($file['name']) . "\"\r\n"
      ."\r\n"
      //Convert the file's binary info into ASCII characters
      .chunk_split(base64_encode($fileContents))
      .$boundary;
    }
  }
  //Now all the attachment data is ready to insert into the email body.
  //If the file was too big for PHP, it may show as having 0 size
  if ($totalAttachmentSize == 0) {
    echo "Message not sent. Either no file was attached, or it was bigger than PHP is configured to accept.";
   }
  //Now make sure it doesn't exceed this function's specified limit:
  else if ($totalAttachmentSize>$maxTotalAttachments) {
    echo "Message not sent. Total attachments can't exceed " .  $maxTotalAttachments . " bytes.";
  }
  //Everything is OK - let's build up the email
  else {    
    $headers =  "From: [email protected]\r\n";
    $headers .=     "MIME-Version: 1.0\r\n"
    ."Content-Type: multipart/mixed; boundary=\"$boundary_text\"" . "\r\n";  
    $body .="If you can see this, your email client "
    ."doesn't accept MIME types!\r\n"
    .$boundary;

    //Insert the attachment information we built up above.
    //Each of those attachments ends in a regular boundary string    
    $body .= $emailAttachments;

    $body .= "Content-Type: text/html; charset=\"iso-8859-1\"\r\n"
    ."Content-Transfer-Encoding: 7bit\r\n\r\n"
    //Inert the HTML message body you passed into this function
    .$htmlMessage . "\r\n"
    //This section ends in a terminating boundary string - meaning
    //"that was the last section, we're done"
    .$boundary_last;

    if(mail($to, $subject, $body, $headers))
    {
      echo "<h2>Thanks!</h2>Form submitted to " . $to . "<br />";
    } else {
      echo 'Error - mail not sent.';
    }
  }    
}

If you want to see what's going on here, comment out the call to mail() and have it echo the output to your screen instead.

like image 150
Nathan Long Avatar answered Nov 15 '22 11:11

Nathan Long