1

弊社はZend(バージョン1.8.4)をベースに独自のCMSを開発しました。新しいバージョンへの切り替えは、今のところ問題外です。

Content-Disposition: inline;埋め込み画像 ( ) とダウンロード可能な添付ファイル ( )を含む (マルチパート) メッセージの送信に Zend Mail を使用していますContent-Disposition: attachment;

数日前、Apple iPhone 5 (内部メール クライアント) でこのようなメールを開く際に問題が発生したという顧客からの報告がありました。しかし、メールを開いた後、添付ファイルが表示されませんでした。この問題は、現在のバージョンの Outlook、Thunderbird、およびさまざまな Web メール クライアントには存在しません。

添付ファイルの有無に応じてメールの Content-Type を変更することで問題を修正しました。

  • メールには、埋め込み画像とダウンロード可能な添付ファイルが含まれています。Content-Type: multipart/mixed;
  • メールには画像が埋め込まれていますが、ダウンロード可能な添付ファイルはありません:Content-Type: multipart/related;

また、さまざまなパーツの境界の組み立てに関して、関数_buildBodyを変更する必要がありました。Zend/Mail/Transport/Abstract.php

では、Zend Mail が送信するメッセージは RFC に準拠していないのではないかと思います。

変更を追加する前 (Apple Mail では機能しません) と変更を追加した後 (ほとんどの一般的なメール クライアントで機能します) のメール構造を次に示します。RFC に準拠しているバージョンを教えてください。

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--

どんな助けでも大歓迎です。

敬具、

ニルス

4

2 に答える 2

4

このメッセージは少し古くなっているようですが、私は zend FW 1 に深く根差したより大きなアプリケーションを動かしているので、この問題を知っています。私は、 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 マルチパート メール メッセージを送信する直前に組み立て、2 つのことを行います。1. $body が組み立てられている部分でわかるように、body html/plan テキスト メッセージを厳密な形式で強制することです。したがって、このコルセットでマルチパート/関連セクションを取得する方法はありません

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

Zend_Mail で HTML 本文部分を操作する唯一の方法では、空白の HTML テキストの代わりに必要なマルチパート MIME を使用できないためです。

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

そのため、自分で手動で作業を行って、単一の部分から完全なマルチパート メールを組み立てることを考える人もいるかもしれません (Zend_Mime_Part と Zend_Mime_Message を使用します)。

ここで 2 番目の問題が発生します。または、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 は空になります) それらが設定されていない場合、エラーがスローされ、Zend_Mail::addPart(Zend_Mime_Part) で追加された、正確に組み立てられたメッセージの手動で追加された Mime_Parts はすべて無視されます。

これを回避するには、プロットされたルーチンの動作を変更して、次のように 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

欠落している 2 番目の混合パートの添付ファイルに注意してください。現在、1 つのパート、つまり multipart/alternative しかありません。この状況では、ZF1 メールは間違った動作をします。なぜなら、ZF1 メールは非常に概念化されているため、Zend_Mime_Part (私のコードの代替部分) を 1 つだけ使用してこの構成を非マルチパート メールとして処理し、必要なマルチパート/代替ヘッダーを削除するからです。ハードアセンブルされた 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 ZF は、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 統合のメリットを享受できます。

于 2015-06-03T21:58:29.433 に答える
3

ZF1 でマルチパート メッセージを使用したことはありませんが、これはメッセージ パーツのネストによって制御する必要があると思います。あなたの説明から(そして常にPDFを添付ファイルとして表示したいと仮定すると)、あなたが望むものは次のとおりです:

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

したがって、メッセージには、メッセージと添付された PDF という 2 つの無関係な部分 (マルチパート/混合) が含まれています。メッセージには同じもの (マルチパート/代替) の 2 つのバージョンが含まれており、それらのバージョンはテキスト バージョンとマルチパート/関連バージョンです。マルチパート関連バージョンには、HTML メッセージとそのインライン画像が含まれています。

私があなたの境界を正しく読んでいる場合 (そして 1 つ欠けているように見える場合)、現在あなたが持っているものは次のとおりです。

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

そのため、厳密なメール クライアントが PDF を無視することは理にかなっています。ヘッダー (マルチパート/関連) は、それがメイン メッセージの一部であることを示している (そしてその中でインラインで参照されている) ためです。

これを見ずにコードで修正できるかどうかを示唆するのは難しいですが、うまくいけば、これが正しい方向に向けられるでしょう。

(余談ですが、ネストされたマルチパート メッセージをサポートしていない ZF2 では、これは絶対に不可能です。)

于 2013-10-21T15:43:57.040 に答える