Obsługa archiwów ZIP w PHP. Część 2: Zapakować?

8 Comments

Po dłuższej przerwie powracamy do poruszonego wcześniej problemu obsługi plików ZIP w PHP. Z archiwizowaniem plików nie jest już tak wesoło, jak można by się spodziewać. Oficjalny manual wspomina o klasie ZipArchiwe. Niestety, w końcu możemy trafić na serwer, który nie spełnia wymagań koniecznych do jej obsługi. W takiej sytuacji należy… napisać własną obsługę pliku ZIP.

Spokojnie, nie będziemy po kolei zastanawiać się co i jak. Wybitnie ambitnych odsyłam do podstawowych informacji na temat plików ZIP oraz dokładnej specyfikacji formatu. W ramach ciekawostki możecie też zerknąć na pierwszą specyfikację. Jak widać, datuje się ona na 1989 rok! Aż przypominają się czasy wpisywania tych cholernych komend w DOSie…

Dosyć nostalgii, przejdźmy do rzeczy. Aby w PHP stworzyć archiwum ZIP, potrzebujemy, oprócz PHP i pakowanych plików, klasę obsługującą proces archiwizacji. W moich wojażach po Sieci natrafiłem na ciekawą klasę autorstwa Johna Coggeshalla, który w swoim artykule wyjaśnił wszystko dosyć dokładnie, więc nie ma sensu powtarzać rzeczy już zrobionych, zwłaszcza, że to nie konstrukcja klasy jest przedmiotem tego artykułu, a jej wykorzystanie.

Próbując skorzystać ze wspomnianej klasy udało mi się stworzyć archiwum, które… nie dało się otworzyć. Z pomocą przyszedł mi Eric Mueller, który przerobił tę klasę do formatu… powiedzmy to: działającego :) Na wszelki wypadek, postanowiłem hostować plik z tą klasą również u siebie – kod jest do ściągnięcia tutaj. Jak już wspominałem, nie sama klasa jest przedmiotem dzisiejszych rozważań, ale jej wykorzystanie, więc przyjrzyjmy się, co mamy do dyspozycji…

zipfile::add_dir($name)
  /* dodawanie nowego katalogu do archiwum */
 
zipfile::add_file($data, $name)
  /* dodawanie nowego pliku do archiwum */
 
zipfile::file()
  /* zwrócenie zawartości archiwum */

Gdzie tu finezja? Brak. Klasa jest prosta, a jej użycie lekkie, łatwe i przyjemne. Warto zauważyć, że jeśli np. chcemy dodać do archiwum pliki a.txt i b.txt i umieścić je w katalogu c w archiwum, musimy najpierw dodać ten katalog, a następnie wspomniane pliki, ze ścieżkami w nazwach:

add_dir("c/");
add_file($zawartosc, "c/a.txt");
add_file($zawartosc, "c/b.txt");

A oto, jak wygląda wykorzystanie naszej klasy zipfile (właściwie nie naszej, tylko Johna Coggeshalla, ale nie czepiajmy się szczegółów, nie jesteśmy na konwencie fantasy) w najprostszym przykładzie:

1
2
3
4
5
6
7
8
9
10
11
12
// wlaczamy do kodu klase zipfile i tworzymy nowy obiekt tej klasy:
include("zipfile.inc.php");
$myZIPfile = new zipfile;
 
// pobieramy zawartosc pliku do zarchiwizowania:
$fileName = "sciezka_i_nazwa_pliku";
$fileHandle = fopen($fileName, "r");
$fileContent = fread($fileHandle, filesize($fileName));
fclose($fileHandle);
 
// dodajemy plik do archiwum:
$myZIPfile->add_file($fileContent, $fileName);

To tyle w kwestii dodawania pliku. Oczywiście moglibyśmy stworzyć sam plik w locie, np:

12
$myZIPfile->add_file("Tekst w moim pliku", "plik.txt");

Co teraz? Mamy do wyboru, albo zapiszemy nasz plik:

13
14
15
16
17
18
// mozemy uzyc wczesniej uzytych nazw zmiennych, gdyz
// zamknelismy juz czytany plik uzywajac fclose()
$fileHandle = fopen("mojPlik.zip", "w");
 
fwrite($fileHandle, $myZIPfile->file());
fclose($fileHandle);

…albo podamy go użytkownikowi (czy może raczej jego przeglądarce):

13
14
15
16
17
18
$archiveName = "TwojPlik.zip";
 
header("Content-type: application/octet-stream");
header("Content-disposition: attachment; filename=" . $archiveName);
 
print $myZIPfile->file();

I w sumie… to z grubsza wszystko. W następnym odcinku z tej serii zobaczymy, jak połączyć to wszystko w logiczną całość, dodając coś jeszcze.

Categories: PHP, Zabawy z kodem Tags: Tagi:, ,

8 thoughts on “Obsługa archiwów ZIP w PHP. Część 2: Zapakować?”

  1. Witam!
    Mam problem linia 8
    $fileContent = fread($fileHandle, filesize($myFile));

    Nie powinno być zamiast $myFile -> $fileName.

    I jednak sa problemy plik zip jest tworzony ale jest pusty.

  2. @Piotr:
    Po kolei…
    1. Oczywiście masz rację. Nieszczęsne $myFile zawieruszyło mi się z innych przykładów. Kod już jest poprawiony.

    2. Co do pustych archiwów – przetestuj najpierw na prostym przykładzie. Może gdzieś wkradł Ci się jakiś prosty błąd. Ja przed chwilą, dla pewności, sprawdziłem kod pakując dwa pliki (.php – takie miałem pod ręką), znajdujące się w tym samym katalogu, co skrypt pakujący. Może problemy ze ścieżkami?

  3. No tak, ale mi bardziej chodzi o to że z innej strony mamy formularz w którym mamy do wyboru np 5 plików. Z tych pięciu plików wybieramy 3 pliki klikamy dajemy DALEJ. Teraz ten skrypt odczytuje które pliki zaznaczyliśmy i je pakuję. Czy mógłbyś mi w tym pomóc? Z góry dzieki.

    1. @Marcin:
      Nie rozumiem, w czym problem. Skrypt ma pobrać z formularza wartości np. checkbox’ów, określających, które pliki użytkownik wybiera.
      Na tej podstawie posiadamy nazwy plików (wartościami checkbox’ów może być jednoznaczne ID, albo nawet nazwa pliku, chociaż tu trzeba uważać) po czym należy zlokalizować każdy z tych plików i dla każdego pliku wykonać archiwizację. Patrz na przykłady. Tu – pierwszy przykład, od linijki 6 – pobranie zawartości pliku i spakowanie go.
      Zapętlić dla tablicy wybranych plików. Potem zwrócić archiwum.

  4. Dzięki za odpowiedz. “Zapętlić dla tablicy wybranych plików. Potem zwrócić archiwum.” No i właśnie tego nie potrafię zrobić. Gdybyś mi pomógł byłbym wdzięczny.

    1. @Marcin:
      Myślę, że zaznajomienie się z podstawami PHP może tu pomóc. Prosisz mnie o opisanie zespołu gotowych rozwiązań, a to nie jest moim celem z założenia. Prezentuję tutaj krótkie (zazwyczaj) gotowe rozwiązania, swoiste “klocki”, z których można poskładać całość, jeśli komuś się chce czegoś nauczyć. Lenistwu mówię stanowcze i zdecydowane “raczej nie” :)

Leave a Reply

Your email address will not be published. Required fields are marked *