这条消息似乎有点过时了,但我知道这个问题,因为我驱动一个更大的应用程序,深深植根于 zend FW 1。不幸的是,Zend_Mail 子系统在我看来在某些地方有点未进化。我认为像swiftmailer这样的 php 库比 ZF 1.12 甚至 ZF2 做得更好。因此,如果不能将您的邮件处理切换到 swiftmailer 并且并行使用ZF 1/ ZF 2,这似乎可以解决这个问题,但出于某些原因,您不希望您像我一样被困在 ZF1 中。
我发现就像 Tim 发布的那样,你的多部分消息的 mime 部分似乎是错误的顺序和包含关系。我找到了这个订单/包含
multipart/mixed
multipart/alternative
text/plain
multipart/related
text/html
image/jpeg
application/pdf
将执行符合 RFC 的工作并将内联图像与其他附件正确集成。但遗憾的是,您无法在 ZF 1 中使用这种结构构建消息。
不可能的原因是 ZF 1 (v1.12) 文件 Zend/Mail/Transport/Abstract.php 中的这部分代码
protected function _buildBody()
{
if (($text = $this->_mail->getBodyText())
&& ($html = $this->_mail->getBodyHtml()))
{
// Generate unique boundary for multipart/alternative
$mime = new Zend_Mime(null);
$boundaryLine = $mime->boundaryLine($this->EOL);
$boundaryEnd = $mime->mimeEnd($this->EOL);
$text->disposition = false;
$html->disposition = false;
**$body = $boundaryLine
. $text->getHeaders($this->EOL)
. $this->EOL
. $text->getContent($this->EOL)
. $this->EOL
. $boundaryLine
. $html->getHeaders($this->EOL)
. $this->EOL
. $html->getContent($this->EOL)
. $this->EOL
. $boundaryEnd;**
$mp = new Zend_Mime_Part($body);
$mp->type = Zend_Mime::MULTIPART_ALTERNATIVE;
$mp->boundary = $mime->boundary();
$this->_isMultipart = true;
// Ensure first part contains text alternatives
array_unshift($this->_parts, $mp);
// Get headers
$this->_headers = $this->_mail->getHeaders();
return;
}
// If not multipart, then get the body
if (false !== ($body = $this->_mail->getBodyHtml())) {
array_unshift($this->_parts, $body);
} elseif (false !== ($body = $this->_mail->getBodyText())) {
array_unshift($this->_parts, $body);
}
**if (!$body) {
/**
* @see Zend_Mail_Transport_Exception
*/
require_once 'Zend/Mail/Transport/Exception.php';
throw new Zend_Mail_Transport_Exception('No body specified');
}**
这个例程在发送之前组装您的 Zend 多部分邮件消息,并且做两件事没有好处。1. 是它强制您的正文 html/plan 文本消息采用严格的形式,正如您在 $body 组装的部分中看到的那样。所以没有办法在这个紧身胸衣中得到你的多部分/相关部分
. $boundaryLine
. $html->getHeaders($this->EOL)
. $this->EOL
. $html->getContent($this->EOL)
. $this->EOL
. $boundaryEnd;
因为您在 Zend_Mail 中操作 html 正文部分的唯一方法不允许您使用所需的多部分 mime 而不是空白 html 文本:
setBodyHtml(string $html, string $charset = null, string $encoding = \Zend_Mime::ENCODING_QUOTEDPRINTABLE) :
因此,人们可能会考虑为自己手动做一些事情并从单个部分组装完整的多部分邮件(Zend_Mime_Part 和 Zend_Mime_Message 用于此目的)。
这里我们进入第二个问题,或者 ZF 可能将其视为功能,我不知道。但是例程中的部分已发布
if (!$body) {
/**
* @see Zend_Mail_Transport_Exception
*/
require_once 'Zend/Mail/Transport/Exception.php';
throw new Zend_Mail_Transport_Exception('No body specified');
禁止多部分邮件配置,其中您没有使用对 Zend_Mail::setBodyHtml 和 Zend_Mail::setBodyText 的调用。(在这种情况下 $body 将是空的)如果它们没有被设置,一个错误将会被抛出并且你所有手动添加的你准确组装的消息的 Mime_Parts,加上 Zend_Mail::addPart(Zend_Mime_Part) 将被简单地忽略。
为了解决这个问题,您必须更改绘图例程的行为以允许多部分消息而不使用 setBodyHtml/setBodyText,如下所示:
if (!$body) {
// this will probably only happen in multipart case
// where we need to assemble manually ..
$this->_isMultipart = true;
// set our manual headers :
$this->_headers = $this->_mail->getHeaders();
return;
/**
* @see Zend_Mail_Transport_Exception
*/
//require_once 'Zend/Mail/Transport/Exception.php';
//throw new Zend_Mail_Transport_Exception('No body specified');
}
在修改 ZF1 代码(取自 v.1.12 Zend/Mail/Transport/Abstract.php)之后,您可以使用自己的结构构建消息。
我将为您举一个示例,说明带有内联图像和其他一些二进制附件的多部分消息的发布定义。我们需要的 mime 嵌套结构是
multipart/mixed
multipart/alternative
text/plain
multipart/related
text/html
image/jpeg
application/pdf
所以我们这样做
// create a "multipart/alternative" wrapper
$mailalternative = new Zend_Mime_Message();
// create a "multipart/related" wrapper
$mailrelated = new Zend_Mime_Message();
// text/plain
$mailplain = new Zend_Mime_Part($textmail);
$mailplain->encoding = Zend_Mime::ENCODING_QUOTEDPRINTABLE;
$mailplain->type = "text/plain; charset=UTF-8";
// add it on right place
$mailalternative->addPart($mailplain);
// text/html
$mailhtml = new Zend_Mime_Part($htmlmail);
$mailhtml->encoding = Zend_Mime::ENCODING_QUOTEDPRINTABLE;
$mailhtml->type = "text/html; charset=UTF-8";
// add it to related part
$mailrelated->addPart($mailhtml);
// try to add some inline img attachments
$img_mimes = array('jpg'=>'jpeg','jpeg'=>'jpeg','png'=>'png');
foreach($attachments as $attachment)
if(isset($img_mimes[strtolower($attachment->Typ)]))
{
$suffix = strtolower($attachment->Typ);
$at = new Zend_Mime_Part($attachment->doc_binary);
$at->filename = $attachment->doc_name.'.'.$attachment->Typ;
$at->type = 'image/'.$img_mimes[$suffix].'; name="'.$attachment->doc_name.'.'.$attachment->Typ.'"';
$at->encoding = Zend_Mime::ENCODING_BASE64;
$at->disposition = Zend_Mime::DISPOSITION_INLINE;
// id is important to address your pics in your html
// part later on. If id = XYZ you will write
// <img src="cid:XYZ"> in your html mail part ...
$at->id = $at->filename;
// add them to related part, so they are accessible in html
$mailrelated->addPart($at);
}
$partrelated= new Zend_Mime_Part($mailrelated->generateMessage());
$partrelated->type = Zend_Mime::MULTIPART_RELATED;
$partrelated->boundary = $mailrelated->getMime()->boundary();
$mailalternative->addPart($partrelated);
$partalternative = new Zend_Mime_Part($mailalternative->generateMessage());
$partalternative->type = Zend_Mime::MULTIPART_ALTERNATIVE;
$partalternative->boundary = $mailalternative->getMime()->boundary();
// default mime type of zend multipart mail is multipart/mixed,
// so here dont need to change type and simply set part:
$mail->addPart($partalternative);
// now try to add binary non inline attachments
$img_mimes = array('jpg'=>'jpeg','jpeg'=>'jpeg','png'=>'png');
foreach($attachments as $attachment)
if(!isset($img_mimes[strtolower($attachment->Typ)]))
{
$at = $mail->createAttachment($attachment->doc_binary);
$suffix = strtolower($attachment->Typ);
$at->type = 'application/'.$suffix;
$at->filename = $attachment->doc_name.'.'.$attachment->Typ;
$at->id = $at->filename;
}
现在您可以像往常一样使用 mail->send() 发送手动组装的多部分邮件;
mail->send();
希望这对需要在更高级情况下使用 ZF1 邮件组件的人有所帮助。
需要注意的一件重要事情:如果您处于一种情况,您只想将内联图像附加到您的邮件中,而没有其他“真实”附件,ZF1 会让您再次陷入困境。我说的是这种情况:
multipart/mixed
multipart/alternative
text/plain
multipart/related
text/html
image/jpeg
注意缺少的第二个混合部分附件,我们现在只有一个部分,即多部分/替代。在这种情况下,ZF1 Mail 会做错,因为它是如此的概念化,以至于它只使用一个 Zend_Mime_Part(我的代码中的替代部分)作为 NON-multipart 邮件处理此配置,并从我们的硬组装 Zend_Mime_Part 对象。(看看 Mail/Transport/Abstract.php _send() 例程
$count = count($this->_parts);
$boundary = null;
...
}
if ($count > 1) {
// Multipart message; create new MIME object and boundary
$mime = new Zend_Mime($this->_mail->getMimeBoundary());
$boundary = $mime->boundary();
} elseif ($this->_isMultipart) {
// multipart/alternative -- grab boundary
$boundary = $this->_parts[0]->boundary;
}
在 Zend/Mime/Message.php isMultiPart() 和 generateMessage() 中,
public function isMultiPart()
{
return (count($this->_parts) > 1);
}
你看到了问题所在。Zend ZF1 仅通过计算添加到 Zend_Mail 对象的部分来确定多部分,但在我们手动组装的情况下这是错误的)
结果是一封不符合您预期的邮件。
幸运的是,对于这个问题/情况有一个简单的解决方法,无需更改 ZF1。
只需在发送邮件之前将默认 Zend_Mail 标头 mime 从 multipart/mixed 更改为 multipart/alternative(只是我们的条带化部分标头)
if($attachcount == 0 && $inlinecount > 0)
$mail->setType('multipart/alternative');
$mail->send();
现在邮件的周围部分已从混合变为替代,对于没有“真实”附件的情况来说这是绝对正确的。
对于所有其他情况,ZF1 本身会根据需要撰写邮件,因此通过处理这些注释,ZF1 v1.12 可以像其他优秀的电子邮件库一样处理复杂的电子邮件配置,并具有 ZF 集成的优势,这对谁来说是有益的。