Csatolt fájl küldése PHP-ben
Talán sokaknak okozott már gondod a PHP kicsit fapados, viszont ennél fogva nagyon jól testre szabható mail() függvénye. Senki se szereti az ide vonatkozó RFC-ket túrni azért, hogy tudjon küldeni egy HTML levelet, vagy hogy egy képet tudjon csatolni a kiküldött levélhez.
A továbbiakban megnézzük, hogyan tudunk felépíteni egy HTML levelet, amihez egy képet csatoltunk hozzá. A dolog ott bonyolódik meg, hogy úgy szeretnénk azt a HTML levelet kiküldeni, hogy akinek az e-mail olvasója nem támogatja az ilyen leveleket az is el tudja majd olvasni. Azaz alternatívaként a levélben meg kell adnunk a sima szöveg változatot is.
A bal oldalamon látható, hogy hogyan fog felépülni az e-mail maga. A dolog lényege, hogy valami egyedi karaktersorozatot tartalmazó elválasztó egységekkel több részre bontjuk a levél tartalmát, és ezeknek a részeknek a típusát és azt, hogy az e-mail olvasó hogy kezelje őket, külön megadhatjuk. Tehát a levél kezdetben így fog kinézni:
--boundary_mixed
ide jön majd a levél szövege
--boundary_mixed
ide jön majd a kép
--boundary_mixed--
A "boundary_mixed" az egyedi azonosító, amivel daraboljuk a levelet. Új blokk kezdésénél két kötőjelet írunk az elválasztó elé, ha pedig le akarjuk zárni az utolsó blokkot, akkor az elválasztó elé és mögé is írunk két kötőjelet. A dolog ott bonyolódik csak meg egy picit, hogy a levél szövegét is több részre kell bontani majd, tehát egy lépéssel később a levél tartalma már így fog kinézni:
--boundary_mixed
Content-Type: multipart/alternative; boundary=boundary_alt
--boundary_alt
a levél sima szövege
--boundary_alt
a levél html szövege
--boundary_alt--
--boundary_mixed
ide jön majd a kép
--boundary_mixed--
Mint látszik, az első blokk elején megadtuk a blokk tartalmának a típusát (ezt majd a többi blokk kezdetnél is meg kell majd adnunk), amiben definiáltunk egy új elválasztó elemet. A multipart/alternative típus tulajdonsága, hogy a benne felsorolt blokkokból azt jeleníti meg, amelyiket a felhasználó e-mail olvasója támogat. Épp ezt akartuk elérni, hogy ha a kliens nem támogatja a HTML alapú leveleket, akkor a sima szöveget kapja meg. A levél tartalma így fog kinézni, miután mindenhol megadtuk a megfelelő fejléc elemeket:
--boundary_mixed
Content-Type: multipart/alternative; boundary=boundary_alt
--boundary_alt
Content-Type: text/plain; charset=iso-8859-2
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline
a levél sima szövege
--boundary_alt
Content-Type: text/html; charset=iso-8859-2
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline
a levél html szövege
--boundary_alt--
--boundary_mixed
Content-Type: image/gif; name=image.gif
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename=image.gif
a kép base64-el elkódolva
--boundary_mixed--
A multipart résztől eltekintve a Content-Type fejléc teljesen hasonlóan viselkedik, mint a HTML-es fejlécek. A Content-Transfer-Encoding határozza meg, hogy milyen formátumban van az adott szöveg abban a blokkban. Kép esetén ez egy base64-es elkódolást jelent, szöveg esetén a quoted-printable értéket használjuk, aminek az esetén a blokk az ASCII karakterkészlet nyomtatható karaktereiből álló karakterláncot fog tartalmazni. A Content-Disposition azt adja meg, hogy a blokk a levél megjelenítésekor hogyan legyen kezelve (csatolt állományként (attachment), vagy jelenjen meg az e-mailben (inline)). A levél tartalma már megvan, most már csak a levél fejlécét kell megadnunk, ami annyiban módosul, hogy ott kell definiálnunk az első elválasztó elemünket (boundary_mixed) és meg kell adnunk a MIME verzióját:
From: a levél feladója
MIME-Version: 1.0
Content-type: multipart/mixed; boundary=boundary_mixed
Nincs más hátra, mint hogy ezt az egészet PHP-ben is megvalósítsuk, ami innentől már nem lesz egy bonyolult dolog:
$files = array('image/gif' => 'image.gif');
$from = 'a levél feladója';
$to = 'a levél címzettje';
$subject = 'a levél témája';
$content = 'a levél tartalma, ami HTML-t is tartalmazhat';
$bd_mixed = md5(uniqid(rand(), true));
$bd_alt = md5(uniqid(rand(), true));
$header =
'From: '.$from.'
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="'.$bd_mixed. '" ';
$text =
'--'.$bd_mixed. '
Content-Type: multipart/alternative; boundary="'.$bd_alt.'"
--'.$bd_alt.'
Content-Type: text/plain; charset=iso-8859-2
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline
'.trim(strip_tags($content)).'
--'.$bd_alt.'
Content-Type: text/html; charset=iso-8859-2
Content-Transfer-Encoding: quoted-printable
Content-Disposition: inline
'.$content.'
--'.$bd_alt.'--
';
foreach ($files as $mime => $name) {
$text .=
'--'.$bd_mixed.'
Content-Type: '.$mime.'; name="'.$name.'"
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="'.$name.'"
'.chunk_split(base64_encode(file_get_contents($name))).'
';
}
$text .= '--'.$bd_mixed.'--';
mail($to, $subject, $text, $header);
Ennyi lenne a kód, mint említettem, nem egy bonyolult dolog, a fentebb írt mail-darabolási ismeret birtokában. Talán csak a chunk_split függvény szorul némi magyarázatra: ha a szövegen kívül nem adunk meg neki más paramétert, akkor a 2045-ös RFC-nek megfelelő szemantika alapján 76 karakterenként tesz egy "\r\n"-t a szövegbe.
Erről ennyit, kellemes csatolt fájlokkal tűzdelt e-mail küldözgetést mindenkinek. :)