Obsługa plików ZIP w PHP
Dzisiaj kolejny artykuł o PHP. Przyznaję, że kazałem Wam czekać dość długo, ale temat przemyślałem i liczę, że zdecydowanej większości z Was się on przyda. Opowiem o "pakowaniu" i "rozpakowywaniu" archiwów ZIP na serwerze przy pomocy PHP i biblioteki ZZipLib.
Dzisiaj kolejny artykuł o PHP. Przyznaję, że kazałem Wam czekać dość długo, ale temat przemyślałem i liczę, że zdecydowanej większości z Was się on przyda. Opowiem o "pakowaniu" i "rozpakowywaniu" archiwów ZIP na serwerze przy pomocy PHP i biblioteki ZZipLib.
Pobieramy i instalujemy ZZipLib
Aby móc obsługiwać archiwa przy pomocy języka PHP, musimy zaopatrzyć się w odpowiednią bibliotekę. ZZipLib jest właśnie taką biblioteką. Plik można pobrać z mojego serwera jak i z sourceforge.net. Osobiście polecam drugą opcję, chociaż w razie problemów ze serwisem, można skorzystać z mojego mirror'a.
Instalacja biblioteki przebiega jak standardowa instalacja programu z plików źródłowych (piszę oczywiście o systemie Linux). Wchodzimy do katalogu gdzie mamy rozpakowaną paczkę z plikami biblioteki. W moim przypadku polecenie wygląda tak:
cd /home/kacper/install/zziplib-0.13.60
Następnie wpisujemy standardowe polecenia używane przy instalacji:
./configure make make install
Kiedy biblioteka zostanie zainstalowana, wystarczy zrestartować serwer i można cieszyć się nowymi możliwościami.
Pakujemy pierwsze archiwum
Aby stworzyć archiwum nie musimy posiadać żadnych plików. Wygenerujemy je programowo. A więc do dzieła!
Najpierw tworzymy obiekt ZipArchive:
$archive = new ZipArchive;
Teraz, musimy wydać polecenie stworzenia nowego archiwum:
$archive->open("nasze_archiwum.zip",ZipArchive::CREATE);
Teraz możemy już dodawać pliki. No tak. Ale głupio by było, gdyby stworzenie pliku zip nie powiodło się, a my próbowalibyśmy dodać do niego później pliki. Dlatego stworzymy jeszcze warunek:
if ($archive->open("nasze_archiwum.zip",ZipArchive::CREATE) === true) {
Teraz w pętli for stworzymy kilka (załóżmy, że pięć) plików z jakimiś danymi. Całość razem z warunkiem if będzie wyglądała tak:
if ($archive->open("nasze_archiwum.zip",ZipArchive::CREATE) === true) {
for ($i=0;$i<5;$i++) { // będziemy tworzyć 5 plików
$f = fopen("plik_" . $i . ".txt","w+"); // tworzymy plik
fwrite($f,"To jest " . $i . " plik testowy!"); // dodajemy treść do pliku
fclose($f); // zamkykamy plik
$archive->addFile("plik_" . $i . ".txt"); // dodajemy przed chwilą stworzony plik do archiwum
print "Dodalem plik: 'plik_" . $i . ".txt' do archiwum! <br />";
}
}
Teraz dodajemy jeszcze instrukcję, która spowoduje zapisanie pliku archiwum i zakończy pracę klasy. Dla przejrzystości i łatwiejszego zrozumienia podam teraz cały kod:
<?php
$archive = new ZipArchive; // tworzymy nowy obiekt ZipArchive
if ($archive->open("nasze_archiwum.zip",ZipArchive::CREATE) === true) { // tworzymy plik
for ($i=0;$i<5;$i++) { // będziemy tworzyć 5 plików
$f = fopen("plik_" . $i . ".txt","w+"); // tworzymy plik
fwrite($f,"To jest " . $i . " plik testowy!"); // dodajemy treść do pliku
fclose($f); // zamkykamy plik
$archive->addFile("plik_" . $i . ".txt"); // dodajemy przed chwilą stworzony plik do archiwum
print "Dodalem plik: 'plik_" . $i . ".txt' do archiwum! <br />";
}
$archive->close(); // kończymy działanie klasy; plik archiwum zostaje zapisany
} else {
print "Wystapil blad przy tworzeniu pliku!";
}
?>
Teraz tylko należy poinstruować skrypt, aby odpowiednio obszedł się z plikami, które wygenerował do stworzenia archiwum. Dodam więc kolejną pętlę za instrukcją $archive->close();, która usunie kolejne pliki:
<?php
$archive = new ZipArchive; // tworzymy nowy obiekt ZipArchive
if ($archive->open("nasze_archiwum.zip",ZipArchive::CREATE) === true) { // tworzymy plik
for ($i=0;$i<5;$i++) { // będziemy tworzyć 5 plików
$f = fopen("plik_" . $i . ".txt","w+"); // tworzymy plik
fwrite($f,"To jest " . $i . " plik testowy!"); // dodajemy treść do pliku
fclose($f); // zamkykamy plik
$archive->addFile("plik_" . $i . ".txt"); // dodajemy przed chwilą stworzony plik do archiwum
print "Dodalem plik: 'plik_" . $i . ".txt' do archiwum! <br />";
}
$archive->close(); // kończymy działanie klasy; plik archiwum zostaje zapisany
for ($i=0;$i<5;$i++) {
unlink("plik_" . $i . ".txt"); // usuwam utworzony plik tekstowy
}
} else {
print "Wystapil blad przy tworzeniu pliku!";
}
?>
Teraz skrypt jest już kompletny. Po jego uruchomieniu możemy cieszyć się jednym plikiem zip :)
Rozpakowywanie danych
Właściwie lepiej by było gdyby ta część była na początku, ponieważ znaleźlibyśmy archiwum do rozpakowania, a następnie w drugiej (czyli aktualnie pierwszej) części byśmy spakowali wypakowane z niego pliki, ale jednak przydatniejszą funkcją jest możliwość spakowania plików, więc część o tym mówiąca jest pierwszą częścią. Pozwolę sobie dać Wam jedną poradę. Unikajcie rozpakowywania wszelkich "paczek", które otrzymujecie na serwer "z zewnątrz", ponieważ mogą one zawierać szkodliwe pliki. Nawet samo przyjęcie takiego pliku na serwer jest ryzykowne, ponieważ istnieją samo-rozpakowujące się archiwa, które także mogą zawierać szkodliwe pliki.
No ale dość dygresji. Zabieramy się za temat. Tak więc, aby rozpakować plik musimy wykonać podobne czynności jak przy pakowaniu, dlatego nie będę pokazywał wszystkiego od początku.
<?php
$archive = new ZipArchive; // tworzymy nowy obiekt ZipArchive
if ($archive->open("nasze_archiwum.zip") === true) { // otwiera plik
if ($archive->extractTo("./") === true) { // rozpakowujemy dane do katalogu, w którym znajduje się plik archiwum
print "Plik zostal rozpakowany!";
} else {
print "Wystapil blad podczas rozpakowywania!";
}
$archive->close(); // kończymy działanie klasy; plik archiwum zostaje zapisany
} else {
print "Wystapil blad przy otwieraniu pliku!";
}
?>
Jak widać, zmieniło się niewiele. Właściwie to zamiast kilku linii tworzących pliki, dodających je do archiwum, a potem usuwających te pliki, użyłem metody extractTo(), którą "obłożyłem" w warunek if. Metoda extractTo() przyjmuje jeden argument, który jest adresem katalogu, do jakiego należy wypakować pliki z archiwum. Ja wpisałem ./, aby przekazać informację, że chcę rozpakować pliki do katalogu, w którym umieszczony jest plik skryptu.
Proste - a jakie przydatne! Teraz możecie już na swoich stronach w działach download umieścić opcję, dzięki której użytkownik wybierze pliki, które chce pobrać, a skrypt stworzy archiwum i wyśle je użytkownikowi. Zachęcam do samodzielnego eksperymentowania i pochwalenia się rezultatami swoich działań w komentarzach.
Przyda się ;)
i mam dla ciebie ambitne zadanie, żeby listingi dało się klikiem rozszerzyć na szerokość całej przeglądarki. albo z jakimś marginesem, żeby ładnie było ;) scrollbary denerwują.
Jak już masz obsługę zip, to przydałoby się coś o pdf.
nie ukrywam że przyda mi się coś na ten temat :D
Widzisz. PDF. To jest pomysł :) Z kodem coś na pewno zrobię. Pytanie tylko, kiedy znajdę na to czas, bo to musi być coś inteligentnego. Może zlikwiduje pola z kodem, żeby zastąpić je linkiem do "kodo-wyświetlacza" z koloryzatorem (oczywiście własnej roboty i na własnym serwerze ;). Jeszcze nie wiem. Ale coś zrobię. Kiedyś.
Niezły art. Klasa na pewno się przyda, ale da się ten kod nieco zoptymalizować, szczególnie jeśli chodzi o generowanie przykładowych plików. fopen, fwrite i fclose można zamienić na prosty file_put_contents. Tak samo do unlink nie potrzeba drugiej pętli - można to przenieść tuż za wrzuceniem pliku do archiwum. Poza tym if(cos===true) to nie przypadkiem to samo co if(cos)? Krócej, a efekt ten sam.
Jeszcze taka mała uwaga: zaleca się stosowanie echo zamiast print (że niby szybsze i nazwa też krótsza ;))
@lisek FPDF
@Comandeer, też pomyślałem o FPDF. Ale mimo wszystko napiszę artykuł o tym. Może nie teraz, ale na pewno napiszę :)