Znalazłem taki artykuł w necie, ale nie wiem czyjego jest autorstwa...
(Nie mój w każdym bądź razie
)Wiele osób staje pewnego dnia przed problemem wysyłania z poziomu php listu elektronicznego z dołączanym do niego plikiem w postaci załącznika. Jak bardzo jest to duży orzech do zgryzienia można stwierdzić przeglądając wszelkiego rodzaju grupy dyskusyjne. Jednak, jak pokaże w poniższym tekście, nie taki diabeł straszny jak go malują :-)
List elektroniczny składa się z dwóch głównych części – nagłówka zawierającego informacje opisujące wiadomość np. Temat (Subject: ),Adresat (To: ), Nadawca (From: ) itp. i treści. Są one oddzielone od siebie pustą linią. W celu przesyłania listu zawierającego załączniki, czyli treść różnego formatu (nie tylko pliki), używa się dodatkowych zasad opisanych przez standard MIME (Multipurpose Internet Mail Extensions). Dodaje on dodatkowe elementy zarówno do nagłówka listu, jaki i do samej treści listu. W nagłówkach można używać następujących pól:
1)MIME-Version: Informacja o wersji formatu MIME używanego w liście. Aktualnie używa się wartości 1.0
2)Content-Type: Określenie typu danych znajdujących się w załączniku np. image/gif, text/plain, text/html
3)Content-Transfer-Encoding:Sposób kodowania danych binarnych np. 7bit, 8bit, binary, quoted-printable, base64. Domyślnie jest przyjmowane kodowanie 7bit. Jeśli chce się używać polskich znaków w formacie iso-8859-2 należy użyć kodowanie 8bit. W wypadku, gdy przesyłamy dane binarne może nastąpić sytuacja, że trafimy na serwer lub program pocztowy, który będzie formatować treść danych (np. zmieniał kodowanie nowej linii z windows na unix) wtedy dane binarne zostaną uszkodzone. Aby uniknąć takich sytuacji są one przekonwertowywane do bezpiecznego formatu np. base64. Niestety takie działania zwiększają wielkość pliku kilkakrotnie, o czym należy pamiętać wysyłając duże załączniki.
4)Content-ID:Pole to określa położenie danych w przypadku, gdy znajdują się one poza dokumentem.
5)Content-Description:Opis treści, należy pamiętać, aby nie używać polskich znaków
6)Content-Disposition:Nagłówek eksperymentalny. Przekazuje on klientowi poczty czy treść ma być wyświetlana, czy oznaczona jako załącznik do dokumentu.
Przy wstawianiu załącznika do listu opakowujemy jego treść w nagłówki informacyjne z powyższej listy. Przy wymienionych polach nie wspomniałem jeszcze o dodatkowych opcjach, których należy używać. Jednak ich prezentacją zajmę się przy omawianiu konkretnych przykładów.
Po tym wstępie teoretycznym przejdźmy do tworzenia listów w formacie MIME. Na początek najprostszy przykład.
Kod
From: ‘Jan Kowalski’
To: 'Ala Kowalska’
Subject: Prosta wiadomosc testowa
Content-Transfer-Encoding: 8bit
Content-type: text/plain; charset=iso-8859-2
MIME-Version: 1.0
Cześć !To ja :-)
Przy tym przykładzie należy wspomnieć o parametrze charset=iso-8859-2. Określa on, jakiego zestawu znaków używamy w wiadomości tekstowej (text/plain). Jeśli wybieramy kodowanie polskich znaczków trzeba pamiętać, aby wstawić Content-Transfer-Encoding: różny od 7bit (np. quoted-printable, 8bit) i zakodować treść w tym formacie. Jest to spowodowane tym że dodatkowe litery języka polskiego mają kody powyżej 7-go bita (127). Dalej prezentuje przykład listu z dwoma załącznikami -tekstowym i binarnym.
Kod
From: 'Jan Kowalski'
To: 'Ala Kowalska’
Subject: =?ISO-8859-2?B?UHJvc3RhIHdpYWRvbW+25iB0ZXN0b3dh?=
Content-type: multipart/mixed; boundary="XX-1234DED00099A";
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
To jest wiadomosc kodowana przy pomocy MIME i jesli to widzisz to twoj klient pocztowy jest glupi :-)
--XX-1234DED00099A
Content-Transfer-Encoding: 8bit
Content-type: text/plain; charset=iso-8859-2
MIME-Version: 1.0
Content-Disposition: inline
Cześć !To ja :-)
--XX-1234DED00099A
Content-Type: image/jpg; name="zdjecie.jpg";
Content-Transfer-Encoding: base64
Content-Description: "Moje zdjecie"
<... zakodowana treść pliku zdjecie.jpg przy pomocy base64 ...>
--XX-1234DED00099A—
Jak widać ten list jest bardziej skomplikowany. W głównym nagłówku listu klient poczty otrzymuje informacje, że treść składa się z wielu wiadomości różnego typu (Content-type: multipart/mixed). W celu poinformowania klienta gdzie zaczynają się i kończą dane poszczególnych załączników używa się parametru boundary="znacznik". Teraz, gdy w nowej linii będziemy mieli ciąg znaków "—znacznik" klient wie, że tutaj kończy się lub zaczyna nowy załącznik. Trzeba pamiętać o dwóch znakach "-" znajdujących się przed ciągiem "znacznik". Przy ostatnim znaczniku mamy dodatkowo dodane na końcu dwa znaki "-" informujące klienta, że to już koniec dokumentu.
W powyższym przykładzie list składa się z dwóch załączników. Pierwszy to zwyczajny tekst kodowany z polskimi znakami (Content-type: text/plain; charset=iso-8859-2), który ma być wyświetlony przez klienta (Content-Disposition: inline). Drugi załącznik to plik w formacie jpg o nazwie zdjecie.jpg (Content-Type: image/jpg; name="zdjecie.jpg";). Jego dane binarne są zakodowane do formatu base64. Może was jeszcze zdziwić postać tytułu listu (pole Subject:). Jest to zakodowany tekst "Prosta wiadomość testowa" z wykorzystaniem polskich znaczków w kodzie iso-8859-2. Takie kodowanie tytułu listu jest zalecane, gdy stosujemy inne kodowanie znaków niż angielskie. Ten dziwny ciąg powstał z następujących elementów:
=? - Znacznik rozpoczęcia kodowanego tekstu
ISO-8859-2 - strona kodowa, w której są zapisane polskie znaczki
?B? - element nagłówka informujący, że do zakodowania tekstu użyto base64, jeśli jest ?Q? to kodowanie quoted-printable
UHJvc3RhIHdpYWRvbW+25iB0ZXN0b3dh - treść tytułu w formacie base64
?= - znacznik końca zakodowanego tekstuNależy wspomnieć jeszcze o kodowaniu quoted-printable, które jest domyślnie używane przez większość programów pocztowych. Pozwala ono zapisać znak z przedziału 8 bitowego na 7 bitach poprzez zastąpienie kodu znaku jego reprezentacją szesnastkową z poprzedzającym znakiem =. Dla przykładu znak o kodzie dziesiętnym 12 zostanie zapisany jako =0C, a znak = (kod dziesiętny 61) zostanie zapisany jako =3D.
Teraz przejdźmy do tego, co tygryski lubią najbardziej, czyli kodowania. Postawmy sobie prosty problem: wysyłanie emailem pliku zapisywanego poprzez stronę www wraz z jego krótkim opisem. Na początek kod html’a odpowiedzialny za wyświetlanie formularza do zapisywania pliku.
<form action="att.php" enctype="multipart/form-data" method="post" > <input type="file" name="plik"> <input type="submit" name="send" value="Send">
Gdy wyślemy zapytanie do serwera nasz skrypt att.php otrzyma trzy zmiennie pochodzące od nazwy obiektu w html'u:
$plik - nazwa pliku tymczasowego zapisanego przez serwer www,
$plik_name - nazwa pliku na dysku komputera klienta (wraz z pełną ścieżką),
$plik_type - typ pliku w formacie MIME np. dla GIF'a - image/gif
Tworzymy teraz funkcje, która dostarczy nam gotowy fragment emaila z zakodowanym plikiem:
<?php
/*
* $tmpfile - ścieżka do pliku tymczasowego zapisanego przez serwer
* $name - nazwa pliku
* $mime - typ pliku
* $bo - znacznik określający początek i koniec danych załącznika
*/
function addAttachment($tmpfile,$name,$mime,$bo) {
$fd = @fopen($tmpfile,'r');
if(!$fd)
return('');
$bin = fread($fd,filesize($tmpfile)); //Odczyt zawartości pliku fclose($fd); $enbin = base64_encode($bin); //Kodowanie pliku do formatu base64 $att = <<<EOT
nContent-Type: $mime; name="$name";
Content-Transfer-Encoding: base64
Content-Disposition: attachment;
$enbin
--$bo
EOT;
return $att;
}
?>
Teraz mając taką funkcję możemy napisać resztę programu.
<?php
$bo = "X----".uniqid(rand()); //Generujemy znacznik oddzielające poszczególne załączniki
if($plik) { //Jeśli jest załadowany plik to go dołączamy
$a = explode('/',$plik_name); //Rozbijamy scieżkę pliku np. c:/moje/dokument.doc
$nazwa = $a[count($a)-1
]; //Wyciągamy ostatni człon scieżki np. dokument.doc
$att = addAttachment($plik,$nazwa,$plik_type,$bo);
}
$head = <<<EOT
From: 'Moja strona' <strona@darmowa.strona.pl>
Mime-Version: 1.0
Content-Type: multipart/mixed; boundary="$bo"
Content-Transfer-Encoding: 7bit
EOT;
$msg =<<<EOT
This is a MIME Encoded Message
--$bo
Content-Type: text/plain; charset=iso-8859-2
Content-Transfer-Encoding: 8bit
To jest treść listu
--$bo$att--
EOT;
$subject = "Moja ładna strona domowa";
$subject = "=?iso-8859-2?B?".base64_encode($subject)."?="; //Kodowanie polskich znaków
mail('ja@mojadomena.pl',$subject,$msg,$head); ?>
Jak widać, jeśli zna się sposób tworzenia listu w formacie MIME napisanie kodu, który to robi jest bardzo proste. W powyższym przykładzie zaprezentowałem tylko podstawowe funkcje używane przy obróbce emaili w php. Bardzo duże możliwości w tym temacie dostarcza moduł IMAP, który ma np. funkcje kodujące do formatu base64, quoted-printable.
Warto zwrócić jeszcze uwagę, że w praktyce, gdy do listu dołączamy pliki znajdujące się na dysku serwera, problematyczne staje się określenie typu MIME dla danego pliku. Rozwiązanie tego problemu jest jednak tematem na następny artykuł :-)
Na koniec wspomnę jeszcze, że przy tworzeniu tego tekstu pomagałem sobie bardzo dobrym artykułem
autorstwa Kartic Krishnamurthy.