<< Part I - Simple HTML Email

PART 2 HTML EMAIL WITH JPEG ATTACHMENTS TUTORIAL
by Jim Plush

In this section we're going to construct a mail script that will send 3 things.

1. A plain text email if the email client does not support MIME type emails
2. An HTML message to the user
3. A JPEG file from my web server


Here is the script that when run will generate such an email (just change the you@yoursevername to whatever email address you want to send it to:


<?PHP  
$to = 'you@yourservername.com';
$subject = 'PHP Mail Attachment Test';
$bound_text = "jimmyP123";
$bound = "--".$bound_text."\r\n";
$bound_last = "--".$bound_text."--\r\n";
   
$headers = "From: admin@server.com\r\n";
$headers .= "MIME-Version: 1.0\r\n"
  ."Content-Type: multipart/mixed; boundary=\"$bound_text\"";
   
$message .= "If you can see this MIME than your client doesn't accept MIME types!\r\n"
  .$bound;
   
$message .= "Content-Type: text/html; charset=\"iso-8859-1\"\r\n"
  ."Content-Transfer-Encoding: 7bit\r\n\r\n"
  ."hey my <b>good</b> friend here is a picture of regal beagle\r\n"
  .$bound;
   
$file = file_get_contents("http://www.litfuel.net/php/regal_004.jpg");
   
$message .= "Content-Type: image/jpg; name=\"regal_004.jpg\"\r\n"
  ."Content-Transfer-Encoding: base64\r\n"
  ."Content-disposition: attachment; file=\"regal_004.jpg\"\r\n"
  ."\r\n"
  .chunk_split(base64_encode($file))
  .$bound_last;

if(mail($to, $subject, $message, $headers))
{
     echo 'MAIL SENT';
} else {
     echo 'MAIL FAILED';
}

?>

Run that and you should get an email with my dog as an attachment,
if you set your email address correctly.

Lets take a run through it line by line:

SECTION 1
$to = 'you@yourservername.com';
$subject = 'PHP Mail Attachment Test';
Starting out the same way as our previous email, setting the To and Subject for our email. Nothing fancy there.

SECTION 2
$bound_text = "jimmyP123";
$bound = "--".$bound_text."\r\n";
$bound_last = "--".$bound_text."--\r\n";

Here is where you want to take notice. Setting the "boundary". In MIME type emails you basically can put as many things as you like in your email as long as they are separated by a boundary.

Consider this:
Plain text email
Html email
JPEG attachment

If you stack those all together in an email how will the MTA(mail transport agent.. IE: sendmail) know when to stop displaying one and display another?

To do that you have to set a boundary.

Plain text email
--boundary
Html email
--boundary
JPEG attachment
--boundary--

Notice the last boundary ends with - that tells the MTA that you are done with your MIME email.

My boundary is set to jimmyP123 Ideally you'll want something that is REALLY unique because you don't want this same sequence appearing anywhere else in your message or your email will split in the wrong place! : I just use jimmyP123 for simplicity. So now that we covered the boundary, lets move on.


SECTION 3
$headers = "From: admin@server.com\r\n";

I'm just setting where I want the email to look like its coming from.


SECTION 4
$headers .= "MIME-Version: 1.0\r\n"
  ."Content-Type: multipart/mixed; boundary=\"$bound_text\"";

Next we append (.=) the headers to add the MIME type. We’re setting it to version 1 which is the current standard version. MIME type is basically saying to the email client you want to send something other than a plain text email (although you can still send plain text with the MIME type set).

We set the Content-Type to multipart/mixed which tells your client that we could be sending a few different email types (html, attachments, plaintext, etc) and we then tell it the boundary name we want to use to seperate each item. Remember all items will be separated with this boundary.


SECTION 5
$message .= "If you can see this MIME than your client doesn't accept MIME types!\r\n"
  .$bound;

Now we begin creating our message. We start out by defining a message with no content type to be displayed if the email client cannot display a MIME type email. Notice we end our email with our boundary. That separates part 1.


SECTION 6
$message .= "Content-Type: text/html; charset=\"iso-8859-1\"\r\n"
  ."Content-Transfer-Encoding: 7bit\r\n\r\n"
  ."hey my <b>good</b> friend here is a picture of regal beagle\r\n"
  .$bound;

We then append (.=) our HTML message. Notice we set the Content-Type to text/html, and our character set right next to it. We then set our Transfer Encoding to 7bit. To find information out on 7bit, base64 and other encodings visit: http://www.faqs.org/rfcs/rfc2045.html I again append my boundary to let the agent know I'm done with that section.


SECTION 7
$file = file_get_contents("http://www.litfuel.net/php/regal_004.jpg");

This line reads an entire file into a string in one shot, its quite a handy function new to PHP 4.3. What we're doing is opening up my jpg file on my webserver and loading into the variable $file. If you var_dump file at this point you'll see quite a bit of information.


SECTION 8
$message .= "Content-Type: image/jpg; name=\"regal_004.jpg\"\r\n"
  ."Content-Transfer-Encoding: base64\r\n"
  ."Content-disposition: attachment; file=\"regal_004.jpg\"\r\n"
  ."\r\n"
  .chunk_split(base64_encode($file))
  .$bound_last;

Here comes the meat and potatoes. We set our Content-Type to image/jpg along with the name of the file being sent, pretty straightforward here, image/jpg tells the client we're sending this as a jpg file and it has the name regal_004.jpg. We then insert a blank line and here comes some tricky stuff.

We run 2 functions on our $file string. Base64_encode and chunk_split. Base64_encode basically just transforms our string of file information into something that a mail client can more easily read or as PHP.net puts it "This encoding is designed to make binary data survive transport through transport layers that are not 8-bit clean, such as mail bodies.".

We then call chunk_split on that base64 encoded string. If you were to var_dump the base64 string you'd see it go on forever and ever, what chunking does is break it up after so many characters so its easier for mail agents to read it. It defaults to 76 characters so after every 76 characters its going to break the line by adding \r\n to it. That's basically all it does. So instead of a string like:

Skjdf89fg98dfg98ad898989h98dgr98dfgu98dfug98dfug8d998u9df98udfg98udfg98udfg98udf9g8udf9g8udfg9df8gudf9g8u9duf

We get
Asdlfkjsadlfkjsakldfj
Ase9435r90r90fgg98
34098340984949484
40930938503948588

Try it yourself
$string = "sdaflk;jasdfl;kjsadfl;jasljsdflkjsdf893u4tr89u8sduf89us98dfu98sduf98u8fu89sudf9usdfsdu89sdufsdf8fus89fusdfu8";
$new_string
= chunk_split($string);
var_dump($new_string);



SECTION 9

if(mail($to, $subject, $message, $headers))
{
     echo 'MAIL SENT';
} else {
     echo 'MAIL FAILED';
}

So now we have a nice format of our jpg file encoded in a way that makes email agents happy, all that's left is to send the message itself and echo out if we're successful.

A note about using mail(). Mail will return true if the email was successfully sent from the server, it has NO IDEA if the email will actually make it or not. You can add an @ in front of mail to not show errors but the better move is to write an error log handler than can keep track of these errors. What good is reporting an error if you never know about it? I hope that you have learned something about sending mail with attachments from this tutorial. If you have any comments you can reach me at: jplush76 (At) gmail.com