Lesson 32 - Attach a file to an email

Features

Our goal here is to send out our standard email with the previously uploaded file attached.

Sounds so simple. Turns out to be nastier than it sounds.

Request Info Email Attachment
Click here to see a larger image

Actions

Arrow Red Right Click to see Lesson32_EmailAttach.php in action on this tutorial site. Note: if you enter name an email in this form, you will send me an email.

Download this file: Lesson32_EmailAttach.txt to your PC. Change:

  • the recipient address
  • the pointers to the style sheet
  • the pointers to the directory where you want the text file
  • the pointers to the directory where you will temporarily store the uploaded file

Then upload all to your website. The “thank yous” should be there from the previous lesson.

Make sure the file permissions on the “directory/file to be written” are correct. Make sure the temporary storage area can be written to. See Gotchas below.

Surf to the Lesson32_EmailAttach.php file on your website with your browser. You should see results similar to the image above on the right.

After completing the form and clicking “Send me more info”:

  • you should receive the usual email with this in the body:
  • Please send me more information about Acme products

    AND your renamed uploaded file attached to the email.

  • your visitor should receive a confirming email
  • a line of text was written to the file
  • the uploaded file (in OUR temporary storage area) is deleted
  • you should be sent to the Thank You page

Gotchas

File permissions. Make sure that your two directories have "world write enabled". Had to change all the “=” in the mail message to something else (I chose “:”.). The “extra” “\n” lines in the mail message are very important. It doesn’t work without them.

Source Code

The format of the mail function is: mail(TO, SUBJECT, MESSAGE, HEADERS). The first three (3) arguments are obvious. HEADERS is not. It turns out that you can do almost anything (make obtuse?) with HEADERS.

We use the unlink function to delete the uploaded file from OUR temporary storage area.

After looking at the source code, you may want to consider using a packaged version of a versatile HTML MIME EMail.

Here is the part of the source code that we really care about.

/* Construct the email message sent to Acme Company  */
$mimeBoundary "Boundary_" .md5(time());
$fullRequestorName $contactFirstName ." " .$contactLastName;
Two minor items above. The $mimeBoundary will be used later in the headers and message portion of the email.
$fp fopen($newUploadedFilePath,"rb");
$myAttachment fread($fp,filesize($newUploadedFilePath));
$myAttachment chunk_split(base64_encode($myAttachment));
fclose($fp);
unlink($newUploadedFilePath);    /* Delete the file from OUR temporary storage area */
Our uploaded file (which we moved and renamed in the previous lesson) is encoded to make it easier to send as an attachment. Then the file is deleted so as not clutter up our website.

$mailAcmeHeaders "From: \"" .$fullRequestorName ."\"<" .$contactEmail .">\n";
$mailAcmeHeaders .= "Reply-to: " .$contactEmail ."\n";
$mailAcmeHeaders .= "MIME-Version: 1.0\n";
$mailAcmeHeaders .= "Content-Type: multipart/mixed; boundary=\"" .$mimeBoundary ."\"\n";

The two new headers tell the recipient’s email reader that the message is MIME compliant and that it contains muliple parts in multiple (in our case, 2) flavors. The $mimeBoundary is used to deliniate the parts of the message.
$messageToAcme "This is a multi-part message in MIME format.\n";
$messageToAcme .= "\n";            /*  This line MUST be here or it won't work  */
Don’t forget the “blank” line. It is critical.
$messageToAcme .= "--" .$mimeBoundary"\n";
$messageToAcme .= "Content-Type: text/plain; charset=\"iso-8859-1\"\n";
$messageToAcme .= "Content-Transfer-Encoding: quoted-printable\n";
$messageToAcme .= "\n";            /*  This line MUST be here or it won't work  */
The first part of the message (the words). The “--” (double dash/hyphen) MUST precede the $mimeBoundary. We tell it that this part is plain text. Don’t forget the blank line.
$messageToAcme .= "Please send me more information about Acme products";
...
$messageToAcme .= "\n\n Comments:\n" $contactComments;
$messageToAcme .= "\n";            /*  This line MUST be here or it won't work  */
The text part of the message. Don’t forget to end it with a blank line.
$messageToAcme .= "--" .$mimeBoundary"\n";
$messageToAcme .= "Content-Type: " .$uploadedFileType ."; name=\"" .$newUploadedFileName ."\"\n"
$messageToAcme .= "Content-Transfer-Encoding: base64\n"
$messageToAcme .= "Content-Disposition: attachment\n";
$messageToAcme .= "\n";            /*  This line MUST be here or it won't work  */
The second part of the message (the attachment). We describe the filetype, the encdoing method and name the attachment.
$messageToAcme .= $myAttachment;
$messageToAcme .= "--" .$mimeBoundary ."--\n";
The attachment (a base64-encoded file). The ending mineBoundary. The trailing “--” is required.
The complete source code
  <?php
  ob_start
();  /* Start buffer so we can use header anywhere in the page  */
  
$fileToBeWritten "../../../Data_Repository/Acme_Demographic_Data.txt";
  
$recipientAcme "dave@marketingtactics.com";
  
$senderAcme  "dave@barnesfamily.com";
  
$tempDirectory "../../../temporary_storage/";  /* This is OUR temporary storage area, not the PHP temp area for uploads  */
?>
  
<html>
<head>
  <title>A Request Info Form attaches an uploaded file to an email.</title>
  <link rel="stylesheet" href="/PHP_Tutorials/Styles/PHP_Tutorials_Forms_Style.css" type="text/css">
</head>

<body>
<h1>Request Information from the Acme Company</h1>
<form method="POST" enctype="multipart/form-data" name="FirstEmailForm" action="<?php echo($PHP_SELF); ?>" >
  <table width="550" border="0" cellspacing="2" cellpadding="0" bgcolor="#DFDFDF">
    <tr>
      <td colspan="2" class="FormCategoryName">About You</td>
    </tr>
    <tr>
      <td width="200" class="FormVariableName" align="right">
        First Name:<span style="color:red">*</span>
      </td>
      <td width="344">
        <input type="text" name="contactFirstName" value="<?php print $_POST['contactFirstName']; ?>" >
        &nbsp;<span class="Footer" style="color:red">* = Required</span>
      </td>
    </tr>
    <tr>
      <td width="200" class="FormVariableName" align="right">
        Last Name:<span style="color:red">*</span>
      </td>
      <td width="344">
        <input type="text" name="contactLastName" value="<?php print $_POST['contactLastName']; ?>" >
      </td>
    </tr>
    <tr>
      <td width="200" class="FormVariableName" align="right">
        Email:<span style="color:red">*</span>
      </td>
      <td width="344">
        <input type="text" name="contactEmail" value="<?php echo $_POST['contactEmail']; ?>" >
      </td>
    </tr>
    <tr>
      <td valign="Top" class="FormCategoryName" colspan="2">Interests</td>
    </tr>
            
      <td align="right" valign="Top" class="FormVariableName">Favorite Characters:</td>
            <td valign="Top">
<?php
if($_POST['contactCharacterBugs'] == "YES")
$cbvalue "checked"; }
else
$cbvalue "unchecked"; }
echo(
"<input type='checkbox' name='contactCharacterBugs' value='YES'" .$cbvalue .">Bugs Bunny<br>");
if(
$_POST['contactDaffy'] == "YES")
$cbvalue "checked"; }
else
$cbvalue "unchecked"; }
echo(
"<input type='checkbox' name='contactDaffy' value='YES'" .$cbvalue .">Daffy Duck<br>");
if(
$_POST['contactRoadRunner'] == "YES")
$cbvalue "checked"; }
else
$cbvalue "unchecked"; }
echo(
"<input type='checkbox' name='contactRoadRunner' value='YES'" .$cbvalue .">Road Runner<br>");
if(
$_POST['contactCoyote'] == "YES")
$cbvalue "checked"; }
else
$cbvalue "unchecked"; }
echo(
"<input type='checkbox' name='contactCoyote' value='YES'" .$cbvalue .">Wiley Coyote");
?>
              </td>
          </tr>
    <tr>
      <td  align="right" valign="Top" class="FormVariableName" width="200"> Age Group:</td>
      <td valign="Top" width="344">
        <select name="contactAgeGroup">
          <option value="NONE">Select One</option>
          <option value="Kid">Child</option>
          <option value="Teen">Teenager</option>
          <option value="TatooFreak">GenX</option>
          <option value="Adult">Adult</option>
          <option value="OldFart">Senior</option>
<?php  
switch ($_POST['contactAgeGroup'])
{
  case 
"Kid":
    print (
"<option value=\"Kid\" selected>Child<option>");
    break;
  case 
"Teen":
    print (
"<option value=\"Teen\" selected>Teenager<option>");
    break;
  case 
"TatooFreak":
    print (
"<option value=\"TatooFreak\" selected>GenX<option>");
    break;
  case 
"Adult":
    print (
"<option value=\"Adult\" selected>Adult<option>");
    break;
  case 
"OldFart":
    print (
"<option value=\"OldFart\" selected>Senior<option>");
    break;
}
?>
        </select>
     </td>
    </tr>
    <tr>
      <td  align="right" valign="Top" class="FormVariableName" width="200">Please add to me to your mailing list:</td>
      <td valign="Top" width="344">
        <?php
if ($_POST['contactAddMailingList'] == "YES")
{
echo (
"<input type='radio' name='contactAddMailingList' value='YES'  checked>YES<br>
       <input type='radio' name='contactAddMailingList' value='No'>No"
);
}
elseif (
$_POST['contactAddMailingList'] == "No")
{
echo (
"<input type='radio' name='contactAddMailingList' value='YES'>YES<br>
       <input type='radio' name='contactAddMailingList' value='No' checked>No"
);
}
else
{
echo (
"<input type='radio' name='contactAddMailingList' value='YES'>YES<br>
       <input type='radio' name='contactAddMailingList' value='No'>No"
);
}
?>
          
       </td>
    </tr>
    <tr>
      <td  align="right" valign="Top" class="FormVariableName" width="200">My attached file:</td>
      <td valign="Top" width="344">
        <input type="hidden" name="MAX_FILE_SIZE" value="500000">  <!-- some systems require that MAX_FILE_SIZE  be set to a certain number of bytes-->
        <input type="file" name="contactUploadedFile">
      </td>
    </tr>
    <tr>
      <td  align="right" valign="Top" class="FormVariableName" width="200">Comments:</td>
      <td valign="Top" width="344">
        <textarea name="contactComments" rows="4" cols="40"><?php print $_POST['contactComments']; ?></textarea>
      </td>
    </tr>
    <tr>
      <td width="200" class="FormVariableName" align="right">
      </td>
      <td width="344">
        <input type="hidden" name="firstPass" value="No">
        <input type="submit" name="subRequestButton" value="Send me more info">
        <br>
        <span class="Footer">Please click only once.</span>
      </td>
    </tr>
  </table>
</form>

<?php
if($_POST['firstPass'] == "No")
{

  
/* Set variables equal to their "posted" values". We need to do this because "register_globals = off" is the default.  */
  
$contactFirstName $_POST['contactFirstName'];
  
$contactLastName $_POST['contactLastName'];
  
$contactCharacterBugs $_POST['contactCharacterBugs'];
  
$contactDaffy $_POST['contactDaffy'];
  
$contactRoadRunner $_POST['contactRoadRunner'];
  
$contactCoyote $_POST['contactCoyote']; 
  
$contactEmail $_POST['contactEmail'];
  
$contactAgeGroup $_POST['contactAgeGroup'];
  
$contactAddMailingList $_POST['contactAddMailingList'];
  
$contactComments $_POST['contactComments'];

  
/* If the user skipped required fields or entered invalid values, write an appropriate error message. */
  
if ($contactFirstName == "")
    exit(
"<p class='PhpError'>Your First Name is missing.</p>");
  if (
$contactLastName == "")
  {
    exit(
"<p class='PhpError'>Your Last Name is missing.</p>");
  }
  if (!(
eregi("^[a-z0-9\._-]+@+[a-z0-9\._-]+\.+[a-z]{2,4}$",$contactEmail))):
    exit(
"<p class='PhpError'>Email Address appears to be invalid.</p>");
  endif;
  
$emailDomain ltrim(strstr($contactEmail'@'), '@');   
  if(!(
checkdnsrr($emailDomainANY)))
    exit(
"<p class='PhpError'>Email Address Domain can not be found on the internet.</p>");

  
/* Ensure that the file was uploaded, renamed and moved to your personal temporary storage area.  */
  /* We rename the uploaded file from filename.ext (the file is assumed to have an extension) to filename_yyyymmdd_xxx.ext, where xxx equals the first 3 letters of the person's last name. */
  /* The whole intent of renaming is to prevent us from receiving the same filenames as attachments. Think about the submit resume case. */
  /* Would you rather receive: resume_20030818_jon.doc and resume_20030819_smi.doc or would you prefer: resume.doc and resume1.doc. Or worse, the second resume.doc overwrites the first resume.doc  */
  /* This method assumes low traffic volume. For higher volumes, you could add minutes or a random number to the date.  */
  
if(!isset($_FILES['contactUploadedFile']))
    exit(
"<p class='PhpError'>The expected file wasn't a part of the submitted form.</p>");
  if(
$_FILES['contactUploadedFile']['error'] != UPLOAD_ERR_OK)
    exit(
"<p class='PhpError'>The file uploaded failed.</p>");
  
$dateForUploadedFile date("Ymd");
  
$firstThreeLettersLastName substr($contactLastName03);
  
$charactersToBeAdded "_" .$dateForUploadedFile ."_" .$firstThreeLettersLastName;
  
$uploadedFileName str_replace(" ""_"$_FILES['contactUploadedFile']['name']);     /* All spaces in filename replaced with underscores  */
  
$positionOfLastPeriodInUploadFileName strrpos($uploadedFileName".");
  
$newUploadedFileName substr_replace($uploadedFileName$charactersToBeAdded$positionOfLastPeriodInUploadFileName0);
  if(!
move_uploaded_file($_FILES['contactUploadedFile']['tmp_name'], $tempDirectory $newUploadedFileName))
    exit(
"<p class='PhpError'>Moving the uploaded file failed.</p>");
  
$uploadedFileType $_FILES['contactUploadedFile']['type'];
  
$newUploadedFilePath =  $tempDirectory $newUploadedFileName;
          
  
/* Construct list of favorite characters  */
  
$favoriteCharactersList "";
  if (
$contactCharacterBugs == "YES")
     
$favoriteCharactersList .= "Bugs Bunny, ";
  if(
$contactDaffy == "YES")
     
$favoriteCharactersList .= "Daffy Duck, ";
  if(
$contactRoadRunner == "YES")
     
$favoriteCharactersList .= "Road Runner, ";
  if(
$contactCoyote == "YES")
     
$favoriteCharactersList .= "Wiley Coyote";
    
  
/* Create Date Time in Human Readable Form */
  
$humanDateTime date("Y") . strtoupper(date("M")) . date("d H:i");

  
/* Replace all LineFeeds within Comments with 2 spaces. */
  
$commentsSingleLine str_replace("\n""  "$contactComments);
  
$commentsSingleLine str_replace("\r""  "$commentsSingleLine);

  
/* Construct the email message sent to Acme Company  */
  
$mimeBoundary "Boundary_" .md5(time());
  
$fullRequestorName $contactFirstName ." " .$contactLastName;
  
$fp fopen($newUploadedFilePath,"rb");
  
$myAttachment fread($fp,filesize($newUploadedFilePath));
  
$myAttachment chunk_split(base64_encode($myAttachment));
  
fclose($fp);
  
unlink($newUploadedFilePath);    /* Delete the file from OUR temporary storage area */
  
  
$mailAcmeHeaders "From: \"" .$fullRequestorName ."\"<" .$contactEmail .">\n";  
  
$mailAcmeHeaders .= "Reply-to: " .$contactEmail ."\n";
  
$mailAcmeHeaders .= "MIME-Version: 1.0\n";
  
$mailAcmeHeaders .= "Content-Type: multipart/mixed; boundary=\"" .$mimeBoundary ."\"\n";
  
  
$messageToAcme "This is a multi-part message in MIME format.\n";
  
$messageToAcme .= "\n";            /*  This line MUST be here or it won't work  */
  
$messageToAcme .= "--" .$mimeBoundary"\n";
  
$messageToAcme .= "Content-Type: text/plain; charset=\"iso-8859-1\"\n";
  
$messageToAcme .= "Content-Transfer-Encoding: quoted-printable\n";
  
$messageToAcme .= "\n";            /*  This line MUST be here or it won't work  */
  
$messageToAcme .= "Please send me more information about Acme products";
  
$messageToAcme .= "\n\n First Name: " $contactFirstName;
  
$messageToAcme .= "\n Last Name: " $contactLastName;
  
$messageToAcme .= "\n Email: " $contactEmail;
  
$messageToAcme .= "\n\n My interests are below:";
  
$messageToAcme .= "\n Favorite Characters: " $favoriteCharactersList;
  
$messageToAcme .= "\n Age Group: " $contactAgeGroup;
  
$messageToAcme .= "\n Add me to mailing list: " $contactAddMailingList;
  
$messageToAcme .= "\n\n Comments:\n" $contactComments;
  
$messageToAcme .= "\n";            /*  This line MUST be here or it won't work  */
  
$messageToAcme .= "--" .$mimeBoundary"\n";
  
$messageToAcme .= "Content-Type: " .$uploadedFileType ."; name=\"" .$newUploadedFileName ."\"\n"
  
$messageToAcme .= "Content-Transfer-Encoding: base64\n"
  
$messageToAcme .= "Content-Disposition: attachment\n";
  
$messageToAcme .= "\n";            /*  This line MUST be here or it won't work  */
  
$messageToAcme .= $myAttachment;
  
$messageToAcme .= "--" .$mimeBoundary ."--\n";
  
  
/* Construct the email message sent to the visitor (the requestor)  */
  
$messageToRequestor "Thanks, " .$contactFirstName .", for requesting information about Acme products";
  
$messageToRequestor .= "\n Your email will totally ignored just like 38% of the F500 ignore emails.";
  
$mailRequestorHeaders "From: \"Acme Wizard\"<"$senderAcme .">\n";  
  
$mailRequestorHeaders .= "Reply-to: " .$senderAcme;

  
  
/* Build the line of data to be appended to the data file */
  /* WARNING - Do not change the order of the items in the file */
  /* This file is imported (using a map) by Marketing */
  
$messageDataLine $humanDateTime "|";
  
$messageDataLine .= "reqinfo" "|";
  
$messageDataLine .= $contactFirstName "|";
  
$messageDataLine .= $contactLastName "|";
  
$messageDataLine .= $contactEmail "|";
  
$messageDataLine .= $favoriteCharactersList "|";
  
$messageDataLine .= $contactAgeGroup "|";
  
$messageDataLine .= $contactAddMailingList "|";
  
$messageDataLine .= $commentsSingleLine "|" "\n";

  
/* Do all the final actions  */
  /* 1. send am email to Acme that includes the uploaded file as an attachment  */
  /* 2. send an email to the requestor  */
  /* 3. write a line to a text (so you have a record of all requests)  */
  /* 4. go to the Thank You (success or faliure) page  */
  
if(mail($recipientAcme"Please Send Acme Info"$messageToAcme$mailAcmeHeaders))
  {
    
mail($contactEmail"Acme Request Received"$messageToRequestor$mailRequestorHeaders);
    
$handleAcmeFile fopen($fileToBeWritten"a");
    
$fwriteSuccess fwrite($handleAcmeFile$messageDataLine);
    
$fcloseSuccess fclose($handleAcmeFile);
    
header("Location: ../../ThankYous/Thanks_Request_Info_Success.html");  /* Redirect browser */
    
exit;
  }
  else
  {
    
header("Location: ../../ThankYous/Thanks_Request_Info_Failure.html");
    
mail("webmaster@marketingtactics.com""Acme, Request Info Failure"$messageToAcme$mailAcmeHeaders);
    exit;
  }
}
?>


</body>
</html>
<?php
  ob_end_flush
();
?>

Final Thoughts

This is way more complicated than it ought to be, in my opinion.

Go To MarketingTactics home