使用PHP发送带附件的HTML邮件

12

我有一个问题:直到今天,我一直使用一个包含以下内容的标题来使用PHP发送HTML邮件:

Content-type: text/html;

现在,我添加了添加附件的功能。为此,我不得不将这行更改为

Content-Type: multipart/mixed;

现在,使用multipart/mixed,邮件中的其余部分——也就是普通文本——只会显示为text/plain。我该如何确保附件能正常工作,同时仍然以HTML形式显示邮件正文?


每个多部分邮件的各个部分都有自己的内容类型,请查看正确的多部分邮件的源代码。 - Rufinus
可能是php发送带附件的电子邮件的重复问题。这里还有很多其他处理电子邮件和附件的问题,请浏览一些相关内容。 - jprofitt
在您的邮件模板中使用 MIME 头。 - Sam Arul Raj T
发送多附件电子邮件,请查看ANDA Anda 05-Sep-2011 11:57的帖子:http://php.net/manual/zh/function.mail.php - Sam Arul Raj T
为什么要重复造轮子?PHP有一个很棒的PEAR扩展,可以发送带附件的邮件。 - j08691
4个回答

27

我尝试了答案1几个小时,但没有成功。我在这里找到了一个解决方案: http://www.finalwebsites.com/forums/topic/php-e-mail-attachment-script

这个解决方法非常好用 - 不到5分钟!你可能想要更改(就像我做的那样)第一个内容类型从text/plain变为text/html。

这是我的稍微修改过的版本,可以处理多个附件:

function mail_attachment($files, $path, $mailto, $from_mail, $from_name, $replyto, $subject, $message) {
$uid = md5(uniqid(time()));

$header = "From: ".$from_name." <".$from_mail.">\r\n";
$header .= "Reply-To: ".$replyto."\r\n";
$header .= "MIME-Version: 1.0\r\n";
$header .= "Content-Type: multipart/mixed; boundary=\"".$uid."\"\r\n\r\n";
$header .= "This is a multi-part message in MIME format.\r\n";
$header .= "--".$uid."\r\n";
$header .= "Content-type:text/html; charset=iso-8859-1\r\n";
$header .= "Content-Transfer-Encoding: 7bit\r\n\r\n";
$header .= $message."\r\n\r\n";

    foreach ($files as $filename) { 

        $file = $path.$filename;

        $file_size = filesize($file);
        $handle = fopen($file, "r");
        $content = fread($handle, $file_size);
        fclose($handle);
        $content = chunk_split(base64_encode($content));

        $header .= "--".$uid."\r\n";
        $header .= "Content-Type: application/octet-stream; name=\"".$filename."\"\r\n"; // use different content types here
        $header .= "Content-Transfer-Encoding: base64\r\n";
        $header .= "Content-Disposition: attachment; filename=\"".$filename."\"\r\n\r\n";
        $header .= $content."\r\n\r\n";
    }

$header .= "--".$uid."--";
return mail($mailto, $subject, "", $header);
}

最新消息...一些服务器在处理\r\n、附件和任何HTML时出现了问题,无法呈现。解决方法是在所有情况下使用PHP_EOL。就这些。 - Brian
我喜欢你的方法。但是我无法将Android APK发送到平板电脑。它发送了APK,但无法安装。 - user462990
1
注意:你的 $path 变量需要在所有 $files 数组中的 $filename 开头或结尾添加 /,并且文件名不能包含空格,或者将 $file = $path.$filename; 更改为 $file = "$path/$filename";。此外,这一行代码 $name = basename($file); 是无意义的,应该删除。 - Jeff Puckett
我的路径变量标准是始终包括尾随斜杠,这是个不错的做法。而且,确实,你找到了一行多余的代码。 - Brian
对于 PHP >=5.6 版本,您需要从头部中分离消息以避免 Multiple or malformed newlines found in additional_header 错误,如 这里 所述。您还需要在第一个 Content-type 后删除双重 \r\n(但这可能是 PHP7 特定的...不确定)。 - Shadi
这个例子根本不起作用。请提供一个可工作的示例,以支持漂亮的HTML消息和符号,如õ ä ö,还有中文和俄文符号。 - user2301515

11

要发送带有附件的电子邮件,我们需要使用multipart/mixed MIME类型来指定在电子邮件中将包含混合类型。此外,我们还想使用multipart/alternative MIME类型来发送电子邮件的纯文本和HTML版本。看一下这个例子:

<?php 
//define the receiver of the email 
$to = 'youraddress@example.com'; 
//define the subject of the email 
$subject = 'Test email with attachment'; 
//create a boundary string. It must be unique 
//so we use the MD5 algorithm to generate a random hash 
$random_hash = md5(date('r', time())); 
//define the headers we want passed. Note that they are separated with \r\n 
$headers = "From: webmaster@example.com\r\nReply-To: webmaster@example.com"; 
//add boundary string and mime type specification 
$headers .= "\r\nContent-Type: multipart/mixed; boundary=\"PHP-mixed-".$random_hash."\""; 
//read the atachment file contents into a string,
//encode it with MIME base64,
//and split it into smaller chunks
$attachment = chunk_split(base64_encode(file_get_contents('attachment.zip'))); 
//define the body of the message. 
ob_start(); //Turn on output buffering 
?> 
--PHP-mixed-<?php echo $random_hash; ?>  
Content-Type: multipart/alternative; boundary="PHP-alt-<?php echo $random_hash; ?>" 

--PHP-alt-<?php echo $random_hash; ?>  
Content-Type: text/plain; charset="iso-8859-1" 
Content-Transfer-Encoding: 7bit

Hello World!!! 
This is simple text email message. 

--PHP-alt-<?php echo $random_hash; ?>  
Content-Type: text/html; charset="iso-8859-1" 
Content-Transfer-Encoding: 7bit

<h2>Hello World!</h2> 
<p>This is something with <b>HTML</b> formatting.</p> 

--PHP-alt-<?php echo $random_hash; ?>-- 

--PHP-mixed-<?php echo $random_hash; ?>  
Content-Type: application/zip; name="attachment.zip"  
Content-Transfer-Encoding: base64  
Content-Disposition: attachment  

<?php echo $attachment; ?> 
--PHP-mixed-<?php echo $random_hash; ?>-- 

<?php 
//copy current buffer contents into $message variable and delete current output buffer 
$message = ob_get_clean(); 
//send the email 
$mail_sent = @mail( $to, $subject, $message, $headers ); 
//if the message is sent successfully print "Mail sent". Otherwise print "Mail failed" 
echo $mail_sent ? "Mail sent" : "Mail failed"; 
?>

从示例中可以看出,发送带附件的电子邮件非常容易。在前面的示例中,我们使用了multipart/mixed MIME类型,并在其中包含了multipart/alternative MIME类型,该类型指定了电子邮件的两个版本。为了将附件包含在我们的消息中,我们从指定的文件中读取数据并将其编码为base64,在将其拆分成较小的块以确保它符合MIME规范,然后将其作为附件包含在内。


17
从其他来源复制粘贴时,包含出处是正确的做法。我甚至不知道这是否是“THE source”,但它们有回溯到2007年的评论:http://webcheatsheet.com/php/send_email_text_html_attachment.php。 - Kalle
1
是的,Sanjay 毫不客气地从另一个网站上剪切并粘贴文本,但没有进行归属。 - user1904273

4

在PHP中,使用SWIFTMAIL可以很好地处理邮件附件。

从这里下载swiftmailer:http://swiftmailer.org/

请看下面的简单代码

包含文件

require_once('path/to/swiftMailer/lib/swift_required.php');

创建传输

//FOR SMTP
// Create the Transport
$transport = Swift_SmtpTransport::newInstance('smtp.googlemail.com', 465, 'ssl')
    ->setUsername('user@gmail.com')
    ->setPassword('gmailpassword');

或者

//FOR NORMAL MAIL
$transport = Swift_MailTransport::newInstance();

邮件发送对象
// Create the Mailer using your created Transport
$mailer = Swift_Mailer::newInstance($transport);

创建消息对象
$message = Swift_Message::newInstance($subject)
    ->setFrom(array($from => $from))
    ->setTo($to)
    ->setBody($body);
$message->attach(Swift_Attachment::fromPath($filepath));

发送消息

$result = $mailer->send($message);

这看起来相当不错,我会看一下的!谢谢 :) - Florian Müller

2
如果您真的想学习如何格式化互联网邮件,那么您应该参考它的请求评论(又称RFC)。定义“多用途Internet邮件扩展 - 互联网邮件正文格式”的是1996年11月发布的RFC2045
格式有些严格,必须按原样遵循。
基本上,邮件包含头部和正文。头部定义了消息类型、格式方式以及其他各种不同的字段。
正文由不同的实体组成。实体可以是纯文本,例如“你好!”但也可以是图像、附件等。
请注意,在以下示例中,所有括在大括号中的内容(例如{hello})都应替换为您的实际值。任何换行符实际上都是CRLF(即ASCII 13 + ASCII 10)。看到两个CRLF时,请坚持不变。这将是展示您创造力的最糟糕的时刻。
基本上,对于带有附件的电子邮件,标题应如下所示:
MIME-Version: 1.0
To: {email@domain}
Subject: {email-subject}
X-Priority: {2 (High)}
Content-Type: multipart/mixed; boundary="{mixed-boudary}"

在上面的例子中,{mixed-boudary}可以是任何唯一的哈希值,比如000008050800060107020705。其他的内容都是不言自明的。
现在,每当我们想要将一个新实体附加到消息中(比如消息正文、图片、附件),我们必须告诉电子邮件代理程序“有一个新部分要来了”,即在该实体前缀中加入{mixed-boundary}的值。我们称之为“打开边界”。请注意,通过打开一个边界,我们不会像最初定义的那样插入该边界,而是在前面再加上2个减号,如--{mixed-boudary}。当我们关闭一个边界时,我们同样要这样做,只是在末尾要使用另外2个减号,如--{mixed-boudary}--。
--{mixed-boudary}
the entity content
--{mixed-boudary}--

因为电子邮件代理应该了解我们新插入实体的内容类型,所以我们必须在边界开头之后声明它。声明只是一个包含与实体兼容的参数/值的标头。

对于HTML正文内容,我的实体标头将如下所示:

Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: 7bit

因此,整个被限定在边界内的身体最终会看起来像:

--{mixed-boudary}
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: 7bit

<html>
<head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head>
<body bgcolor="#FFFFFF" text="#000000">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque vel 
dapibus arcu. Duis quam dui, ornare non mi nec, luctus faucibus massa. Vivamus 
quis purus in erat euismod ullamcorper vitae eget dolor. Aliquam tempor erat 
accumsan, consectetur ex et, rhoncus risus.
</body>
</html>

如果需要插入另一个实体,则按照上述步骤进行操作。当消息没有更多数据添加时,我们关闭混合边界,即CRLF + --{mixed-boudary}--。
如果由于任何原因必须插入一种替代表示形式的实体(例如,一个主体消息既以纯文本格式插入,也以HTML格式插入),则实体内容必须声明为multipart / alternative内容类型(尽管全局multipart/mixed头仍然存在!)。每个备用表示都将由此新边界括起来。
以下是完整示例:
MIME-Version: 1.0
To: {email@domain}
Subject: {email-subject}
X-Priority: {2 (High)}
Content-Type: multipart/mixed; boundary="{mixed-boudary}"

--{mixed-boudary}
Content-Type: multipart/alternative; boundary="{alternative-boudary}"

--{alternative-boudary}
Content-Type: text/plain; charset=utf-8;
Content-Transfer-Encoding: 7bit

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque vel 
dapibus arcu. Duis quam dui, ornare non mi nec, luctus faucibus massa. Vivamus 
quis purus in erat euismod ullamcorper vitae eget dolor. Aliquam tempor erat 
accumsan, consectetur ex et, rhoncus risus.

--{alternative-boudary}
Content-Type: text/html; charset=utf-8;
Content-Transfer-Encoding: 7bit

<html>
<head><meta http-equiv="content-type" content="text/html; charset=utf-8"></head>
<body bgcolor="#FFFFFF" text="#000000">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque vel 
dapibus arcu. Duis quam dui, ornare non mi nec, luctus faucibus massa. Vivamus 
quis purus in erat euismod ullamcorper vitae eget dolor. Aliquam tempor erat 
accumsan, consectetur ex et, rhoncus risus.
</body>
</html>

--{alternative-boudary}--

--{mixed-boudary}
Content-Type: application/pdf; name="myfile.pdf"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="myfile.pdf"

JVBERi0xLjINOCAwIG9iag08PCAvTGVuZ3RoIDkgMCBSIC9GaWx0ZXIgL0ZsYXRlRGVjb2Rl
ID4+DXN0cmVhbQ1oQ51bbY/cNg7+BfsfhAUO11w3riW/B7gPaZEAAdpcm06RL8EBzoyn68uM
vZ3xZLv//khKsuUxNaMNiiabpUg+pKiHsmxJEcN/UsgiilP4ab2/+XF1I81vszSqclHIOEpj
sdrf/PC2EFVUpmK1vXkZxVKs1uJlJJVYPYrvPra7XVvvxYdIrE7rL83hhVj97+bNyjUoFam7
FnOB+tubGI3FZEkwmhpKXpVRnqJi0PCyjBJ1DjyOYqWBxxXp/1h3X+ov9abZt434pV0feoG/
ars/xU/9/qEZmm7diJ+abmgOr0TGeFNFEuXx5M4B95Idns/QAaJMI1IpKeXi9+ZhaPafm4NQ
cRwzNpK0iirlRvisRBZpVJa+PP51091kkjBWBXrJxUuZRjIXh0Z8FN3MnB5X5st5Kay9355n

--{mixed-boudary}--

提示

使用您喜欢的电子邮件客户端(我的是Thunderbird),向自己发送一封纯文本、一封仅包含HTML内容、一封混合内容的邮件,以及每个之前的邮件都附带一个文件附件。当您收到该邮件时,请查看其源代码(查看 -> 消息源)。

@编辑:可以在此处找到一个非常详细记录的案例研究和PHP示例。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接