多部分消息,包括与Zend Mail兼容的多个附件(“附件"和“内联")–是否符合RFC? [英] Multipart messages including multiple attachments ("attachment" and "inline") with Zend Mail – RFC-compliant?

查看:76
本文介绍了多部分消息,包括与Zend Mail兼容的多个附件(“附件"和“内联")–是否符合RFC?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们公司基于Zend(版本1.8.4)开发了自己的CMS.暂时无法切换到新版本.

Our company has developed its own CMS based on Zend (version 1.8.4). Switching to a new version is out of the question for the moment.

我们正在使用Zend Mail发送(多部分)带有嵌入式图像(Content-Disposition: inline;)和可下载附件(Content-Disposition: attachment;)的邮件.

We are using Zend Mail for sending (multipart) messages with embedded images (Content-Disposition: inline;) and downloadable attachments (Content-Disposition: attachment;).

几天前,一位客户报告说在他的Apple iPhone 5(内部邮件客户端)上打开此类邮件时出现问题:确实在收件箱中,该邮件已用符号标记,表明该邮件带有附件.但是,打开邮件后,附件不可见.当前版本的Outlook,Thunderbird和各种Webmail客户端中不存在该问题.

A few days ago, a customer reported problems opening such a mail on his Apple iPhone 5 (internal mail client): In the inbox the mail was indeed marked with a symbol indicating that the mail has attachments. However, after opening the mail, the attachment was not visible. The problem does not exist in the current versions of Outlook, Thunderbird and various webmail clients.

我通过根据附件的存在来更改邮件的Content-Type来解决此问题:

I fixed the problem by changing the Content-Type of the mail depending on the presence of attachments:

  • 邮件包含嵌入式图像和可下载的附件:Content-Type: multipart/mixed;
  • 邮件包含嵌入式图像,但没有可下载的附件:Content-Type: multipart/related;
  • mail contains embedded images and downloadable attachments: Content-Type: multipart/mixed;
  • mail contains embedded images but no downloadable attachments: Content-Type: multipart/related;

我还不得不更改Zend/Mail/Transport/Abstract.php中关于不同部分的边界组装的函数_buildBody.

I also had to change the function _buildBody in Zend/Mail/Transport/Abstract.php regarding the assembly of boundaries for the different parts.

因此,我想知道Zend Mail是否发送不符合RFC的消息.

So, I wonder if Zend Mail sends messages that are not RFC-compliant.

这是添加我的更改之前(不适用于Apple Mail)和之后(适用于大多数常见邮件客户端)的邮件结构.您能告诉我哪个版本符合RFC吗?

Here is the mail structure before (does not work with Apple Mail) and after (works in most common mail clients) adding my changes. Could you please tell me which version is RFC-compliant?

Zend Mail标准结构(不适用于Apple Mail):

Content-Type: multipart/related; charset="utf-8"; boundary="=_0a0dbd2691e7728ea0f689fba0366bed"
MIME-Version: 1.0

--=_0a0dbd2691e7728ea0f689fba0366bed
Content-Type: multipart/alternative; boundary="=_a70ea5862a6842785870a9a4d003a2a7"
Content-Transfer-Encoding: 8bit

--=_a70ea5862a6842785870a9a4d003a2a7
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

[MAIL_TEXT]

--=_a70ea5862a6842785870a9a4d003a2a7
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable

[MAIL_HTML]

--=_a70ea5862a6842785870a9a4d003a2a7--

--=_0a0dbd2691e7728ea0f689fba0366bed
Content-Type: application/pdf
Content-Transfer-Encoding: base64
Content-ID: <test.pdf>
Content-Disposition: attachment; filename="test.pdf"

[PDF_ATTACHED]

Content-Type: image/jpeg
Content-Transfer-Encoding: base64
Content-ID: <test.jpg>
Content-Disposition: inline; filename="test.jpg"

[IMAGE_EMBEDDED]

--=_0a0dbd2691e7728ea0f689fba0366bed--

Zend Mail自定义结构(适用于大多数常见的邮件客户端):

Content-Type: multipart/mixed; charset="utf-8"; boundary="=_8ab337ec2e38e1a8b82a01a5712a8bdb"
MIME-Version: 1.0

--=_8ab337ec2e38e1a8b82a01a5712a8bdb
Content-Type: multipart/related; boundary="=_HTML60dd2cb7fc955f6c8a626c92c76aa2db"
Content-Transfer-Encoding: 8bit

--=_HTML60dd2cb7fc955f6c8a626c92c76aa2db
Content-Type: multipart/alternative; boundary="=_ALTd40db860af4718399b954c403d0b0557"
Content-Transfer-Encoding: 8bit

--=_ALTd40db860af4718399b954c403d0b0557
Content-Type: text/plain; charset=utf-8
Content-Transfer-Encoding: quoted-printable

[MAIL_TEXT]

--=_ALTd40db860af4718399b954c403d0b0557
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable

[MAIL_HTML]

--=_ALTd40db860af4718399b954c403d0b0557--

--=_HTML60dd2cb7fc955f6c8a626c92c76aa2db
Content-Type: image/jpeg
Content-Transfer-Encoding: base64
Content-ID: <gemeinschaft.jpg>
Content-Disposition: inline; filename="gemeinschaft.jpg"

[IMAGE_EMBEDDED]

--=_HTML60dd2cb7fc955f6c8a626c92c76aa2db--

--=_8ab337ec2e38e1a8b82a01a5712a8bdb
Content-Type: application/pdf
Content-Transfer-Encoding: base64
Content-ID: <glamus-test-schnellwarnung.pdf>
Content-Disposition: attachment; filename="glamus-test-schnellwarnung.pdf"

[PDF_ATTACHED]

--=_8ab337ec2e38e1a8b82a01a5712a8bdb--

感谢您的帮助.

亲切的问候,

指甲

推荐答案

该消息似乎有些过时了,但是我知道这个问题,因为我驱动了一个扎根于zend FW 1的更大的应用程序.我在某些地方没有改变. 我认为像 swiftmailer 这样的php库比ZF 1.12甚至ZF2做得更好. 因此,如果不选择将邮件处理切换为swiftmailer,则同时使用 ZF 1 /ZF 2 似乎可以解决这个问题,出于某些原因,不希望您像我一样被困在ZF1中.

This message seems to be a bit outdated, but I know this problem, because I drive a bigger application deeply rooted with zend FW 1. Unfortunately Zend_Mail subsystem seems to me a bit unevolved on some places. I presume php libraries like swiftmailer do thisjob much better than ZF 1.12 and maybe even ZF2. So, if switching your mail processing to swiftmailer is not an option and parallel usage of ZF 1/ ZF 2, which seems to give solutions to this problem, is of some reasons not desired you are stuck within ZF1, just like me.

我发现,就像蒂姆(Tim)发布的一样,您的多部分消息的哑剧部分似乎处于错误的顺序和包含关系.我发现这个顺序/包含

I found out that just like Tim posted, mime parts of your multipart message seem to be in wrong order and inclusion relation. I find this order/inclusion

multipart/mixed
multipart/alternative
    text/plain
    multipart/related
        text/html
        image/jpeg
application/pdf

将进行符合RFC的工作,并将内嵌图像与其他附件正确集成.但是可悲的是,您不能使用这种结构来构建消息 在ZF 1中.

would do a RFC-compliant work and correctly integrate inline images with other attachments. But sadly, you can't build messages with this structure in ZF 1.

之所以无法实现,原因是ZF 1(v1.12)文件中的这部分代码 Zend/Mail/Transport/Abstract.php

The reason, why it is not possible is this part of code in ZF 1 (v1.12) file 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组装部分中所见.所以没有办法 您在此紧身胸衣中的多部分/相关部分

This routine assembles your Zend multipart Mail messages right before sending and does two things for no good. 1. is that it forces your body html/plan text message in a strict form, as you can see in the part, where $body is assembled. So there is no way to get your multipart/related section in this corset

              . $boundaryLine
              . $html->getHeaders($this->EOL)
              . $this->EOL
              . $html->getContent($this->EOL)
              . $this->EOL
              . $boundaryEnd;

因为您在Zend_Mail中唯一的操作html正文部分的方法不允许您使用所需的multi-mime代替空白html文本:

since your only method in Zend_Mail to manipulate html body part do not allow you to use the multipart mime required instead of blank html text :

setBodyHtml(string $html, string $charset = null, string $encoding = \Zend_Mime::ENCODING_QUOTEDPRINTABLE) : 

因此,可能有人会想到手动为自己做事,并从单个部分组装完整的多部分邮件(为此使用Zend_Mime_Part和Zend_Mime_Message).

So, than one may think of doing stuff manually for your self and assemble your full multipart mail from singular parts (Zend_Mime_Part and Zend_Mime_Message are to use for this).

在这里,我们要解决第二个问题,否则ZF可能会将其视为功能,我不知道. 但是例行程序中的部分已发布

Here we come in to second problem, or ZF may see it as feature, i don't know. But the part in routine posted

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将为空),如果未设置它们,将引发错误,并且将完全忽略您手动添加的,由Zend_Mail :: addPart(Zend_Mime_Part)准确组装的消息中的Mime_Parts.

prohibits a multipart mail configuration where you have not used calls to Zend_Mail::setBodyHtml and Zend_Mail::setBodyText. (In this case $body would be empty) If they are not set, an Error will be thrown and all your manually added Mime_Parts of your accurately assembled message, added with Zend_Mail::addPart(Zend_Mime_Part) will be simply ignored.

要解决此问题,您必须更改绘制例程的行为,以允许多部分消息,而无需使用setBodyHtml/setBodyText,例如:

To get around this, you have to change behavior of the plotted routine to allow multipart messages without usage of setBodyHtml/setBodyText like this :

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)之后,您可以构建 具有您自己的结构的消息.

After this modification of ZF1 code (taken from v.1.12 Zend/Mail/Transport/Abstract.php) you are able to build messages with your own structure.

我将为您提供带内嵌图像和其他一些二进制附件的多部分消息的已发布定义示例.我们要求的mime嵌套结构是

I will give you an example for posted definition of multipart message with inline images and some other binary attachments. Our required mime nesting structure is

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();

Now you can send your manually assembled multi part mail as usual with an mail->send();

mail->send(); 

希望这可以帮助需要在更高级的情况下使用ZF1的邮件组件的人.

Hope this helps people that need to use mail component of ZF1 in more advanced situations.

需要注意的一件事:如果您处在某种情况下, 仅将内嵌图像附加到您的邮件中,而不附加其他真实"附件, ZF1会让您再次遇到麻烦. 我说的是这种情况:

One important thing to note: If you are in a situation, where you want to attach only inline images to your mail but no other "true" attachments, ZF1 will make you trouble again.. I'm talking of this situation :

multipart/mixed
multipart/alternative
    text/plain
    multipart/related
        text/html
        image/jpeg

请注意缺少第二个混合部分的附件,我们现在只有一个部分, 即多部分/替代. 在这种情况下,ZF1 Mail会做错事情,因为它的概念如此之深,以至于它仅使用一个Zend_Mime_Part(我代码的替代部分)作为NON-multipart邮件来处理此配置,并剥离所需的内容 硬组装的Zend_Mime_Part对象中的multipart/alternate头. (看看Mail/Transport/Abstract.php _send()例程

Note the missing second mixed part attachment, we now have only one part, namely the multipart/alternative. In this situation, ZF1 Mail will do it wrong, because it is so concept-ed that it handles this configuration with only one Zend_Mime_Part (the alternative part from my code) as NON-multipart mail and strip the required multipart/alternative header from our hard assembled Zend_Mime_Part object. (Take a look at Mail/Transport/Abstract.php _send() routine

    $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(),

and in Zend/Mime/Message.php isMultiPart() and generateMessage(),

    public function isMultiPart()
{
    return (count($this->_parts) > 1);
}

你看到了 问题. Zend ZF1只需计算添加的零件即可确定多部分 到Zend_Mail对象,但这在我们手动组装的情况下是错误的)

there you see the problem. Zend ZF1 determines multipart simply in counting added parts to Zend_Mail object, but this is wrong in our manually assembled situation)

结果是一封不符合您预期的邮件.

Result is a mail that is not what you supposed.

幸运的是,对于此问题/情况,有一个简单的解决方法,而无需更改ZF1.

Luckily there is an easy workaround for this problem/situation, without need to change ZF1.

在发送之前,只需将默认的Zend_Mail标头mime从multipart/mixed更改为multipart/alternative(仅是我们剥离的part标头) 您的邮件

Simply change the default Zend_Mail header mime from multipart/mixed to multipart/alternative (just our striped away part header) before sending your mail

      if($attachcount == 0 && $inlinecount > 0)
          $mail->setType('multipart/alternative');
      $mail->send();

现在,邮件的周围部分已从混合更改为替代,这对于没有真"附件的情况绝对是正确的.

Now surrounding part of mail has changed from mixed to alternative, what is absolutely correct for a situation with no "true" attachments.

在所有其他情况下,ZF1都会根据需要编写邮件,因此,在处理本说明时,ZF1 v1.12可以像处理其他良好的电子邮件库一样处理复杂的电子邮件配置,并具有ZF集成的优势好处.

For all other situations ZF1 natively does compose mail as required, so that with taking care of this notes, ZF1 v1.12 can handle complex email configurations just like other good email libraries and carries advantages of ZF integration for whom this is of benefit.

这篇关于多部分消息,包括与Zend Mail兼容的多个附件(“附件"和“内联")–是否符合RFC?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆