jörg: mail mit eingebettetem Bild versenden

Hallo alle miteinander,

ich versuche gerade, eine mail mit eingebettetem Bild zu versenden. Das ganze soll so ablaufen, daß in der mail zuerst das Bild und darunter ein Text angezeigt werden soll. Ich habe daher einen Script im Internet gefunden, den ich ergänzt habe. Leider hat die Sache aber einen Haken:

1. Das Bild erscheint zwar in der mail, wird aber als "Anhang" behandelt. Es soll aber komplett eingebettet sein.

2. Der abschließende Text "Hier folgt weiterer Text" erscheint nicht.

Ich weiß einfach nicht mehr weiter. Könnt ihr mir helfen? Wenn es geht, bitte auch gleich den geänderten Quelltext mit angeben. Danke :-))

$Empfaenger = "x.y@z.de";
$Betreff = "Meine erste Mail mit Anhang";
$Dateiname = "poster.gif";
$DateinameMail = "Poster.gif";
$Header = "From: Mein Name meine@mailadresse.de";
$Trenner = md5(uniqid(time()));
$Header .= "\n";
$Header .= "MIME-Version: 1.0";
$Header .= "\n";
$Header .= "Content-Type: multipart/mixed; boundary=$Trenner";
$Header .= "\n\n";
$Header .= "This is a multi-part message in MIME format";
$Header .= "\n";
$Header .= "--$Trenner";
$Header .= "\n";
$Header .= "Content-Type: text/html";
$Header .= "\n";
$Header .= "Content-Transfer-Encoding: 8bit";
$Header .= "\n\n";
$Header .= "<body text='#FFFFFF' bgcolor='#000000'>";
$Header .= "\n";
$Header .= "--$Trenner";
$Header .= "\n";
$Header .= "Content-Type: image/gif; name=$DateinameMail";
$Header .= "\n";
$Header .= "Content-Transfer-Encoding: base64";
$Header .= "\n";
$Header .= "Content-Disposition: inline; filename=$DateinameMail";
$Header .= "\n\n";
$Dateiinhalt = fread(fopen($Dateiname, "r"), filesize($Dateiname));
$Header .= chunk_split(base64_encode($Dateiinhalt));
$Header .= "\n";
$Header .= "--$Trenner--";
$Header .= "\n\n";
$Header .= "Content-Type: text/html";
$Header .= "\n\n";
$Header .= "<b>Hier folgt weiterer Text";
mail($Empfaenger, $Betreff, "", $Header);

  1. Hallo Jörg,

    ich versuche gerade, eine mail mit eingebettetem Bild zu versenden. Das ganze soll so ablaufen, daß in der mail zuerst das Bild und darunter ein Text angezeigt werden soll.

    also wirst du nicht vermeiden können, das Ding als HTML-Mail zu verschicken. Denn andernfalls hast du nur den Grundaufbau Text+Attachment(s) ohne eine feste, vorgegebene Reihenfolge oder Beziehung.

    Ich habe daher einen Script im Internet gefunden, den ich ergänzt habe.

    *EIN* Script, nicht EINEN.
    Und jetzt ein paar Anmerkungen zum Code.

    $Empfaenger = "x.y@z.de";
    $Betreff = "Meine erste Mail mit Anhang";
    $Dateiname = "poster.gif";
    $DateinameMail = "Poster.gif";

    Wozu verschiedene Dateinamen? Und dann noch mit der ungewöhnlichen (und ungeliebten) Großschreibung?

    $Header = "From: Mein Name meine@mailadresse.de";
    $Trenner = md5(uniqid(time()));
    $Header .= "\n";

    Bingo, Fehler. Als Zeilenumbruch in Mail-Headern ist die Sequenz CR+LF vorgeschrieben. Die meisten MTAs akzeptieren zwar auch ein einzelnes LF, aber darauf darf man sich nicht verlassen.
    Außerdem ein guter Rat: Es macht den Code übersichtlicher, wenn man den Zeilenumbruch gleich in derselben Zeile mit angibt, anstatt ihn separat im Script zu verstreuen:

    $Header = "From: Mein Name meine@mailadresse.de\r\n";

    $Header .= "Content-Type: multipart/mixed; boundary=$Trenner";
    $Header .= "\n\n";

    Richtiger wäre hier der Content-Type "multipart/related". Dein Ansatz mit "multipart/mixed" würde heißen, dass mehrere Teile vorliegen, die alle gleichrangig sind, aber nichts miteinander zu tun haben (z.B. Text und mehrere Anhänge).

    Außerdem beendest du mit dem doppelten Zeilenumbruch die Header, was danach folgt, ist eigentlich der Mail-Inhalt (mail body). Diesen ebenfalls noch dem "Header" zuzuordnen, ist zumindest irreführend. Technisch macht es keinen Unterschied, weil die Bestandteile nachher sowieso alle sequentiell aufeinander folgen, aber es ist halt unsauber.

    $Header .= "This is a multi-part message in MIME format";
    $Header .= "\n";
    $Header .= "--$Trenner";
    $Header .= "\n";
    $Header .= "Content-Type: text/html";
    $Header .= "\n";
    $Header .= "Content-Transfer-Encoding: 8bit";
    $Header .= "\n\n";
    $Header .= "<body text='#FFFFFF' bgcolor='#000000'>";

    Spannende Frage: Wie referenzierst du das Bild innerhalb deines HTML-Teils? Richtig wäre die Form  <img src="cid:identifier" ... >, wobei identifier ein beliebig wählbarer String ist, der im nächsten Teil der Mail (dem Bildanhang) wieder auftaucht. Schau dir das mal im Quellcode einer HTML-Mail an, die z.B. mit Outlook oder Outlook Express erzeugt wurde. Das Prinzip dürfte schnell deutlich werden.

    $Header .= "\n";
    $Header .= "--$Trenner";
    $Header .= "\n";
    $Header .= "Content-Type: image/gif; name=$DateinameMail";
    $Header .= "\n";
    $Header .= "Content-Transfer-Encoding: base64";
    $Header .= "\n";
    $Header .= "Content-Disposition: inline; filename=$DateinameMail";
    $Header .= "\n\n";

    Anstatt "Content-Disposition:" genügt hier ein einfaches "Content-ID: identifier", wobei hier genau der String definiert wird, mit dem du im HTML-Teil das Bild referenzierst. Einen Dateinamen braucht das Bild hier nicht, weil es über die Content-ID (cid) direkt referenziert wird.

    $Header .= "--$Trenner--";
    $Header .= "\n\n";
    $Header .= "Content-Type: text/html";
    $Header .= "\n\n";
    $Header .= "<b>Hier folgt weiterer Text";

    Warum das? Diesen "weiteren Text" kannst du doch bereits im ersten HTML-Teil unterbringen. Ganz abgesehen davon, dass ein HTML-Dokument mit <html> oder besser noch mit einem <!DOCTYPE> beginnt. HTML-Mails mit mehreren HTML-Teilen sind AFAIK nicht vorgesehen, und außerdem sagst mit mit den zwei '-' hinter dem $Trenner, dass der Block "multipart/*" hier zu Ende ist - deshalb wohl auch:

    1. Der abschließende Text "Hier folgt weiterer Text" erscheint nicht.

    mail($Empfaenger, $Betreff, "", $Header);

    Und hier sieht man nochmal, wie du die Zuordnung von Header und Inhalt durcheinanderbringst: Du versendest eine Mail ohne Inhalt, aber dafür mit elend langem Header. Das funktioniert nur deshalb, weil du den doppelten Zeilenumbruch bereits *innerhalb* der Header hast und so die Zuordnung wiederherstellst. Schön ist das aber nicht.

    So long,
     Martin

    --
    Kleine Geschenke erhalten die Freundschaft.
    Große verderben sie aber meist auch nicht.
    1. Das Bild erscheint zwar in der mail, wird aber als "Anhang" behandelt. Es soll aber komplett eingebettet sein.

    2. Der abschließende Text "Hier folgt weiterer Text" erscheint nicht.

    Dein Format ist falsch, vergleiche mal dein Werk mit dem unten angehängten.

    From: Mein Name meine@example.com
    MIME-Version: 1.0
    Content-Type: multipart/mixed; boundary=$Trenner

    This is a multi-part message in MIME format
    --$Trenner
    Content-Type: text/html
    Content-Transfer-Encoding: 8bit

    <body text='#FFFFFF' bgcolor='#000000'>
    --$Trenner
    Content-Type: image/gif; name=$DateinameMail
    Content-Transfer-Encoding: base64
    Content-Disposition: inline; filename=$DateinameMail

    [Dateiinhalt]
    --$Trenner--

    Content-Type: text/html

    <b>Hier folgt weiterer Text

    From: bla
    MIME-Version: 1.0
    Content-Type: multipart/related; boundary="MIME-Trenner"

    This is a multi-part message in MIME format.
    --MIME-Trenner
    Content-Type: text/html; charset=us-ascii
    Content-Transfer-Encoding: 7bit

    <html>
    <head>
      <meta content="text/html;charset=ISO-8859-1" http-equiv="Content-Type">
    </head>
    <body>
    Eine Zeile mit Bild: <img src="cid:Bild-ID">. Da war gerade das Bild.
    </body>
    </html>

    --MIME-Trenner
    Content-Type: image/gif; name="bild.gif"
    Content-Transfer-Encoding: base64
    Content-ID: <Bild-ID>
    Content-Disposition: inline; filename="bild.gif"

    [Dateiinhalt]

    --MIME-Trenner--

    ("MIME-Trenner" und "Bild-ID" sollten natürlich eindeutige Zeichenketten sein, so wie du sie schon benutzt hast, ich habe das hier nur der Übersichtlichkeit halber vereinfacht.) (Du hättest dir so eine Blaupause auch selbst erstellen können, indem du dir einfach selbst eine entsprechende Mail geschrieben und dann in den empfangenen Quellcode geschaut hättest.)

    Wie dir auffallen sollte, werden die Bestandteile nicht zerstückelt (du hast das Bild einfach mitten in den Text gewürgt), sondern sauber getrennt und über den Wert in Content-ID bzw. die URL cid verbunden.

    Zweitens folgen die Kopfdaten direkt auf den MIME-Trenner, erst dann eine Leerzeile, die den Kopf vom Inhalt trennt (bei der zweiten Hälfte bzw. beim dritten Teil hast du eine Leerzeile zu viel).

    Drittens ist multipart/mixed für unabhängige Teile gedacht, bei dir hängt aber der Text mit dem Bild zusammen, daher multipart/related.

    Viertens:

    mail($Empfaenger, $Betreff, "", $Header);

    Ich hätte nicht gedacht, dass überhaupt etwas ankommt, denn deine E-Mail hat eigentlich gar keinen Inhalt (dritter Parameter ist leer). Dem Lehrbuch nach hätten die ersten drei Zeilen des Beispiels als Parameter 4, der gesamte Rest als Parameter 3 übergeben werden sollen.

    Siehe auch RFC 2045 und 2046 (insbesondere Abschnitt 5), sowie 2387 (multipart/related).

    1. Hallo,

      ist es nicht witzig, dass wir fast zur gleichen Zeit fast das Gleiche zu sagen hatten?

      Schönen Sonntag noch,
       Martin

      --
      Kleine Geschenke erhalten die Freundschaft.
      Große verderben sie aber meist auch nicht.
  2. Super, danke für Eure Hilfe.

    Ich habe mal den Script, dank Eurer Ratschläge, geändert. Heraus kam dann der folgende untenstehende Script. Der dritte Parameter steht immer noch auf "". Aber das scheint Thunderbird und outlook express nicht zu stören. Eigentlich wüßte ich auch nicht genau, was ich in den dritten Parameter genau reinschreiben sollte. Habe das nicht so richtig verstanden .....

    <?php
    $Empfaenger = "x.y@z.de";
    $Betreff = "Meine erste Mail mit Anhang";
    $Dateiname = "Poster.gif";
    $DateinameMail = "Poster.gif";
    $Header = "From: Mein Name meine@mailadresse.de";
    $Trenner = md5(uniqid(time()));
    $Header .= "\r\n";
    $Header .= "MIME-Version: 1.0";
    $Header .= "\n";
    $Header .= 'Content-Type: multipart/related; boundary="MIME-Trenner"';
    $Header .= "\r\n";
    $Header .= "This is a multi-part message in MIME format";
    $Header .= "\n";
    $Header .= "--MIME-Trenner";
    $Header .= "\n";
    $Header .= "Content-Type: text/html; charset=us-ascii";
    $Header .= "\n";
    $Header .= "Content-Transfer-Encoding: 7bit";
    $Header .= "\r\n";
    $Header .='Das ist ein Test<p>&nbsp;</p><p>&nbsp;</p><p align="center"><img src="cid:1234code4321">';
    $Header .= "\n";
    $Header .="<p>&nbsp;</p><p>&nbsp;</p><b>Und das auch ...";
    $Header .= "\n";
    $Header .= "--MIME-Trenner";
    $Header .= "\n";
    $Header .= "Content-Type: image/gif; name=$DateinameMail";
    $Header .= "\n";
    $Header .= "Content-Transfer-Encoding: base64";
    $Header .= "\n";
    $Header .= "Content-Disposition: inline; filename=$DateinameMail";
    $Header .= "\n";
    $Header .="Content-ID: <1234code4321>";
    $Header .= "\r\n";
    $Dateiinhalt = fread(fopen($Dateiname, "r"), filesize($Dateiname));
    $Header .= chunk_split(base64_encode($Dateiinhalt));
    $Header .= "\n";
    $Header .= "--MIME-Trenner--";
    mail($Empfaenger, $Betreff, "", $Header);
    ?>

    1. Hallo,

      Ich habe mal den Script, ...

      immer noch "den"? Im Deutschen heißt es DAS Script.

      Der dritte Parameter steht immer noch auf "". Aber das scheint Thunderbird und outlook express nicht zu stören.

      Natürlich nicht, die bekommen ja auch nichts davon mit, wie du deinen Mail-Quellcode zusammenbaust. Das sieht nur der PHP-Interpreter auf deinem Server.

      Eigentlich wüßte ich auch nicht genau, was ich in den dritten Parameter genau reinschreiben sollte. Habe das nicht so richtig verstanden .....

      Also nochmal: Alles was du VOR dem doppelten Zeilenumbruch hast, gehört zum Header (vierter Parameter). Was NACH dem doppelten Zeilenumbruch kommt, ist der Mail Body, auch Message Text genannt und gehört formal gesehen als dritter Parameter übergeben.
      Deine Version funktioniert deshalb, weil für den endgültigen Versand sowieso die Header und der Message Text zu EINEM String verbunden werden. Deswegen habe ich dir klarzumachen versucht, dass dies ein formaler Fehler ist, der aber für das Funktionieren nicht relevant ist.

      <?php
      $Empfaenger = "x.y@z.de";
      $Betreff = "Meine erste Mail mit Anhang";
      $Dateiname = "Poster.gif";
      $DateinameMail = "Poster.gif";
      $Header = "From: Mein Name meine@mailadresse.de";
      $Trenner = md5(uniqid(time()));
      $Header .= "\r\n";
      $Header .= "MIME-Version: 1.0";
      $Header .= "\n";

      Hier fehlt \r.

      $Header .= 'Content-Type: multipart/related; boundary="MIME-Trenner"';
      $Header .= "\r\n";

      Hier möchten zwei \r\n stehen, weil hier der Header zu Ende ist. Alles, was hier noch folgt, gehört genaugenommen als dritter Parameter an mail() übergeben.

      $Header .= "This is a multi-part message in MIME format";
      $Header .= "\n";

      Hier fehlt wieder \r.

      $Header .= "--MIME-Trenner";
      $Header .= "\n";

      Hier auch.

      $Header .= "Content-Type: text/html; charset=us-ascii";
      $Header .= "\n";

      Und hier auch.

      $Header .= "Content-Transfer-Encoding: 7bit";
      $Header .= "\r\n";
      $Header .='Das ist ein Test<p>&nbsp;</p><p>&nbsp;</p><p align="center"><img src="cid:1234code4321">';
      $Header .= "\n";

      Hier ... ach, du weißt schon. Und noch ein paarmal. ;-)

      $Header .="<p>&nbsp;</p><p>&nbsp;</p><b>Und das auch ...";
      $Header .= "\n";
      $Header .= "--MIME-Trenner";
      $Header .= "\n";
      $Header .= "Content-Type: image/gif; name=$DateinameMail";

      Der Zusatz name=... kann entfallen, er hat keinerlei Funktion.

      $Header .= "\n";
      $Header .= "Content-Transfer-Encoding: base64";
      $Header .= "\n";
      $Header .= "Content-Disposition: inline; filename=$DateinameMail";

      Content-Disposition ist bei inline referenzierten Objekten auch überflüssig.

      $Header .= "\n";
      $Header .="Content-ID: <1234code4321>";
      $Header .= "\r\n";
      $Dateiinhalt = fread(fopen($Dateiname, "r"), filesize($Dateiname));
      $Header .= chunk_split(base64_encode($Dateiinhalt));
      $Header .= "\n";
      $Header .= "--MIME-Trenner--";
      mail($Empfaenger, $Betreff, "", $Header);
      ?>

      So long,
       Martin

      --
      Wenn alle das täten, wass sie mich können,
      käme ich gar nicht mehr zum Sitzen.