PHP multiple attachment email shows up as text
=======UPDATE========
The problem ended up being that I was using the header ContentType when I should have been using Content-Type.
Thanks for your help guys.
Derp derp derp.
=======/UPDATE=======
I apologize in advance for the obnoxiously long question.
I'm attempting to send emails with attachments through PHP. I've done this successfully before with a single attachment. But I can't get it to work with multiple attachments, or for that matter now with a single attachment using this method. So, what am I doing wrong?
I understand that there are other libraries for these functions available. But I want to know WHY this one is not working.
Here is the method that generates the actual email:
function gen_mail ()
{
$files = $this->files();
$has_attachments = (bool) $files;
$EOL = "\r\n"; // tried \n, PHP_EOL
$message = $this->message;
$headers = $this->gen_headers();
$headers .= "MIME-Version: 1.0$EOL";
if ($has_attachments) {
$hash = md5(time());
$sep = "=={$hash}==";
$headers .= "ContentType: multipart/mixed; boundary=\"$sep\"$EOL$EOL";
$headers .= "This is a MIME-encoded message.$EOL$EOL";
$headers .= "--$sep$EOL";
}
$headers .= "Content-Type: text/html; charset=\"iso-8859-1\"$EOL";
$headers .= "Content-Disposition: inline$EOL";
$headers .= "Content-Transfer-Encoding: 7bit$EOL$EOL";
$headers .= "$message$EOL$EOL";
if ($has_attachments) { // here go attachments
foreach ($files as $key => $value) {
$filename = is_numeric($key)? basename($value) : $key;
$file = fopen($value, 'rb');
$data开发者_运维技巧 = fread($file, filesize($value));
fclose($file);
$content = chunk_split(base64_encode($data));
$headers .= "--$sep$EOL";
$headers .= "Content-Type: application/octet-stream; ";
$headers .= "name=\"$filename\"$EOL";
$headers .= "Content-Transfer-Encoding: base64$EOL";
// tried Content-Disposition, attachment, and every combination
$headers .= "Content-Description: inline; $EOL ";
$headers .= "filename=\"$filename\"$EOL$EOL";
$headers .= "$content$EOL$EOL";
}
$headers .= "--$sep--$EOL$EOL";
}
return $headers;
}
And this is what the headers returned by the above function look like:
From: test@urbido.es
MIME-Version: 1.0
ContentType: multipart/mixed; boundary="==cd42448edf10bca4640bc7d6365e1961=="
This is a MIME-encoded message.
--==cd42448edf10bca4640bc7d6365e1961==
Content-Type: text/html; charset="iso-8859-1"
Content-Disposition: inline
Content-Transfer-Encoding: 7bit
Travis, you are so cool!!!1!
--==cd42448edf10bca4640bc7d6365e1961==
Content-Type: application/octet-stream; name="html.login.php"
Content-Transfer-Encoding: base64
Content-Description: inline;
filename="html.login.php"
PD9waHANCgkNCiRoZWFkZXJfY29uZmlnID0gYXJyYXkoKTsNCiRtc2cgPSAnUmVnaXN0ZXJlZCBP
cGVyYXRvcnMsIGVudGVyIHlvdXIgT3BlcmF0b3IgSUQgYW5kIHBhc3N3b3JkIHRvIGxvZ2luJzsN
CiRtc2dfY2xhc3MgPSAnbm9ybWFsJzsNCmlmICgkbG9nZ2VkX2luKSB7DQoJJGhlYWRlcl9jb25m
aWdbJ2NvbnRyb2xzJ10gPSBhcnJheSAoDQoJCWFycmF5ICgndGV4dCcgPT4gJ1N1Ym1pdCBFbnRy
aWVzJywgJ2hyZWYnID0+ICdlbnRyeScpLA0KCSk7DQoJJG1zZyA9ICdZb3UgYXJlIGxvZ2dlZCBp
bi4gVXNlIHRoZSBhYm92ZSBsaW5rcywgb3IgDQoJCWNsaWNrIDxhIGhyZWY9ImVudHJ5Ij5oZXJl
PC9hPiB0byBiZWdpbiBvciByZXN1bWUgYSBqb2InOw0KCSRtc2dfY2xhc3MgPSAnc3VjY2Vzcyc7
DQp9DQoNCj8+
--==cd42448edf10bca4640bc7d6365e1961==--
I've tried functions written by other people, and they produce seemingly identical output. But when I send those headers with mail($address, $subject, '', $headers)
I get this in my inbox:
This is a MIME-encoded message.
--==cd42448edf10bca4640bc7d6365e1961==
Content-Type: text/html; charset="iso-8859-1"
Content-Disposition: inline
Content-Transfer-Encoding: 7bit
Travis, you are so cool!!!1!
--==cd42448edf10bca4640bc7d6365e1961==
Content-Type: application/octet-stream; name="html.login.php"
Content-Transfer-Encoding: base64
Content-Description: inline;
filename="html.login.php"
PD9waHANCgkNCiRoZWFkZXJfY29uZmlnID0gYXJyYXkoKTsNCiRtc2cgPSAnUmVnaXN0ZXJlZCBP
cGVyYXRvcnMsIGVudGVyIHlvdXIgT3BlcmF0b3IgSUQgYW5kIHBhc3N3b3JkIHRvIGxvZ2luJzsN
CiRtc2dfY2xhc3MgPSAnbm9ybWFsJzsNCmlmICgkbG9nZ2VkX2luKSB7DQoJJGhlYWRlcl9jb25m
aWdbJ2NvbnRyb2xzJ10gPSBhcnJheSAoDQoJCWFycmF5ICgndGV4dCcgPT4gJ1N1Ym1pdCBFbnRy
aWVzJywgJ2hyZWYnID0+ICdlbnRyeScpLA0KCSk7DQoJJG1zZyA9ICdZb3UgYXJlIGxvZ2dlZCBp
bi4gVXNlIHRoZSBhYm92ZSBsaW5rcywgb3IgDQoJCWNsaWNrIDxhIGhyZWY9ImVudHJ5Ij5oZXJl
PC9hPiB0byBiZWdpbiBvciByZXN1bWUgYSBqb2InOw0KCSRtc2dfY2xhc3MgPSAnc3VjY2Vzcyc7
DQp9DQoNCj8+
--==cd42448edf10bca4640bc7d6365e1961==--
You've got the wrong header. It's "Content-Disposition", not "Content-Description". Try:
Content-Disposition: attachment; filename="html.login.php"
instead. Content-Description is to add a short DESCRIPTION of what the attachment is, e.g. "This is my resume" and has nothing to do at all with telling the mail client how to deal with the attachment.
I know this is a solved issue, but I wnated to post a working function for all who might need it in the future. There are many example over net for sending multi-attachments email but most of them are confusing and non-working. I've got turibe function (I hope you do not mind) and reworked a little bit.
// priority: 1 (Highest), 2 (High), 3 (Normal), 4 (Low), 5 (Lowest).
function mail_attachment($files,$mailto,$from_mail,$from_name,$subject,$message,$bcc=false,$cc=false,$priority="3") {
$EOL = "\r\n";
$datetime=new DateTime('now', new DateTimeZone('Europe/Sofia'));
//sanitize
$from_mail=filter_var($from_mail, FILTER_SANITIZE_EMAIL);
$mailto=filter_var($mailto, FILTER_SANITIZE_EMAIL);
$subject = str_replace(array("\r\n", "\r", "\n", "\t"), '',$subject);
$from_name = str_replace(array("\r\n", "\r", "\n", "\t"), '',$from_name);
$bcc = str_replace(array("\r\n", "\r", "\n", "\t"), '',$bcc);
$cc = str_ireplace(array("\r\n", "\r", "\n", "\t"), '', $cc);
$headers = "From: ".$from_name." <".$from_mail.">$EOL";
$headers .= "Message-ID: <".md5(uniqid(microtime()))."@".gethostname().">$EOL";
$headers .= "Date: ".$datetime->format(DateTime::RFC2822).$EOL;
$headers .= "X-Mailer: PHP/".phpversion().$EOL;
$headers .= "X-Sender: ".$_SERVER['REMOTE_ADDR'].$EOL;
$headers .= "X-Priority: ".$priority.$EOL;
$headers .= "MIME-Version: 1.0$EOL";
if($bcc)$headers .= "Bcc:".$bcc."\r\n";
if($cc)$headers .= "Cc:".$cc."\r\n";
if($files){
$message=nl2br(trim(filter_var(stripslashes($message), FILTER_SANITIZE_STRING)));
$hash = md5(time());
$sep = "=={$hash}==";
$headers .= "Content-Type: multipart/mixed; boundary=\"$sep\"$EOL$EOL";
$headers .= "This is a MIME-encoded message.$EOL$EOL";
$headers .= "--$sep$EOL";
$headers .= "Content-Type: text/html; charset=\"iso-8859-1\"$EOL";
$headers .= "Content-Disposition: inline$EOL";
$headers .= "Content-Transfer-Encoding: 7bit$EOL$EOL";
$headers .= "$message$EOL$EOL";
//here go attachments
foreach ($files as $key => $value) {
$filename = is_numeric($key)? basename($value) : $key;
$file = fopen($value, 'rb');
$data = fread($file, filesize($value));
fclose($file);
$content = chunk_split(base64_encode($data));
$headers .= "--$sep$EOL";
$headers .= "Content-Type: application/octet-stream; ";
$headers .= "name=\"$filename\"$EOL";
$headers .= "Content-Transfer-Encoding: base64$EOL";
$headers .= "Content-Description: inline; $EOL ";
$headers .= "filename=\"$filename\"$EOL$EOL";
$headers .= "$content$EOL$EOL";
}
$headers .= "--$sep--$EOL$EOL";
}else{
$message=trim(filter_var(stripslashes($message), FILTER_SANITIZE_STRING));
}
$r=mail($mailto, $subject, $message, $headers);
return $r;
}
I use a shared host and have not been able to figure out how to use the PEAR mail features - everything I've read seems to assume that the reader already knows how to incorporate PEAR with an include('Mail.php') - but nobody says how to obtain the Mail.php file. Urgh.
Anyway, my apps send emails infrequently and only to a few recipients at a time. So I feel that using php's mail utility is fine.
Also, sometimes my attachments are dynamically generated so I left the chunk_split outside of the method so I can easily attach an actual file or just as easily generate the data and attach it as a file.
Here's a simple email class that has actually been tested:
<?php
class MyMail {
private function getMimeType ($fileName) {
$mime_types = array("pdf"=>"application/pdf","zip"=>"application/zip","csv"=>"text/plain","doc"=>"application/msword"
,"docx"=>"application/vnd.openxmlformats-officedocument.wordprocessingml.document"
,"xls"=>"application/vnd.ms-excel","xlsx"=>"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
,"ppt"=>"application/vnd.ms-powerpoint","pptx"=>"application/vnd.openxmlformats-officedocument.presentationml.presentation"
,"gif"=>"image/gif","png"=>"image/png","jpeg"=>"image/jpg","jpg"=>"image/jpg","mp3"=>"audio/mpeg","wav"=>"audio/x-wav"
,"mpeg"=>"video/mpeg","mpg"=>"video/mpeg","mpe"=>"video/mpeg","css"=>"text/css","js"=>"application/javascript"
,"php"=>"text/plain","htm"=>"text/html","html"=>"text/html","txt"=>"text/plain");
$extension = explode('.',$fileName);
$extension = end($extension);
$extension = strtolower($extension);
if(isset($mime_types[$extension]))
return $mime_types[$extension];
return "application/octet-stream";
}
private $to = '';
private $subject = '';
private $returnPath = '';
private $attachments = array();
private $recipients = array('from' => '', 'reply' => '');
private $message = '';
public function setTo($s) { // comma separated list
$this->to = $s;
}
public function setSubject($s) {
$this->subject = $s;
}
public function setFrom($s) { // one email address only!
$this->recipients['from'] = "From: $s";
}
public function setReturnPath($s) { // one email address only!
$this->returnPath = "-r $s";
}
public function setReplyTo($s) {
$this->recipients['reply'] = "Reply-To: $s";
}
public function setCc($s) { // comma separated list
$this->recipients['cc'] = "Cc: $s";
}
public function setBcc($s) { // comma separated list
$this->recipients['bcc'] = "Bcc: $s";
}
public function setMessage($s) { // be carefull to include strategic nl and spaces for text only presentation -- which uses strip_tags()
$this->message = $s;
}
public function setAttachment($fileName, $data) {
$this->attachments[] = array('name' => $fileName, 'data' => $data);
}
public function send() {
$attachments = ( count($this->attachments) > 0 );
$altMarker = '===_Alt_BOUNDRY_' . md5(uniqid(time()));
$mixedMarker = str_replace('Alt', 'Mixed', $altMarker);
$txtMsg = strip_tags($this->message);
$htmlMsg = "<!DOCTYPE html>\n<html>\n<head>\n";
$htmlMsg .= '<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">';
$htmlMsg .= "\n</head>\n<body>\n";
$htmlMsg .= $this->message;
$htmlMsg .= "\n</body>\n</html>";
$h = array();
preg_match('/<([^<>]+)>$|^\bFrom: ([^<>]+)\b$/',$this->recipients['from'], $a);
$nakedFrom = !empty($a[1]) ? $a[1] : $a[2];
if( empty($this->returnPath) )
$this->returnPath = '-r ' . $nakedFrom;
if( empty($this->recipients['reply']) )
$this->recipients['reply'] = 'Reply-To: ' . str_replace('From: ','',$this->recipients['from']);
foreach ($this->recipients as $v)
$h[] = $v;
$h[] = "MIME-Version: 1.0";
if( $attachments ) {
$h[] = 'Content-Type: multipart/mixed; boundary="' . $mixedMarker . '"';
$h[] = '';
$h[] = '--' . $mixedMarker;
}
$h[] = 'Content-Type: multipart/alternative; boundary="' . $altMarker . '"';
$h[] = '';
$h[] = '--' . $altMarker;
$h[] = 'Content-Type: text/plain; charset=iso-8859-1';
$h[] = 'Content-Transfer-Encoding: 7bit';
$h[] = '';
$h[] = $txtMsg;
$h[] = '--' . $altMarker;
$h[] = 'Content-Type: text/html; charset=iso-8859-1';
$h[] = 'Content-Transfer-Encoding: 7bit';
$h[] = '';
$h[] = $htmlMsg;
$h[] = '--' . $altMarker . '--';
$h[] = '';
foreach( $this->attachments as $v ) {
$h[] = '--' . $mixedMarker;
$h[] = 'Content-Type: ' . $this->getMimeType ($v['name']) . ';';
$h[] = ' name="' . $v['name'] . '"';
$h[] = 'Content-Transfer-Encoding: base64';
$h[] = 'Content-Disposition: attachment;';
$h[] = ' filename="' . $v['name'] . '"';
$h[] = ''; /* skip */
$h[] = $v['data'];
}
if( $attachments )
$h[] = '--' . $mixedMarker . '--';
return (mail( $this->to, $this->subject, null, implode("\r\n", $h ), $this->returnPath) ? true : false);
}
}
*- USAGE: *
$mail = new MyMail;
$mail->setSubject( ' Testing 123 Subject' );
$mail->setFrom( 'me <testFrom@abc.com>' );
$mail->setTo( 'Anne Test <atest@example.com>' );
$mail->setCc('Jeff Test <jtest@example.com>, Mary Test <mtest@example.com>');
$mail->setMessage( "<h1>This is a test</h1>\n\n<p>This is a para</p>" ); // note the nl added in case mail opens as pure text...
// dynamically produced file data
$mail->setAttachment( $fileName1,
chunk_split(base64_encode($attachmentData1)) );
// attach actual file
$mail->setAttachment( $fileName2,
chunk_split(base64_encode(file_get_contents('testFile.pdf'))) );
echo ( $mail->send() ) ? 'sent' : 'oops';
精彩评论