Zend_Form i wysyłanie plików

by Mateusz Tymek — on Zend Framework, PHP

Head's up! This post was written back in 2009 and is very likely to contain outdated information.

Temat wysyłania plików był już przeze mnie poruszany wcześniej, jednak myślę że teraz warto do niego powrócić. Tym razem zagadnienie zostanie opisane w cyklu czterech artykułów. Pierwszy przedstawi prosty przykład tradycyjnego, synchronicznego wysyłania plików. Drugi zaprezentuje wysyłanie w tle, a trzeci - pasek postępu oparty na rozszerzeniu APC. W ostatnim pokażę jak zintegrować wysyłanie z klasami MVC Zenda.

Zapraszam na część pierwszą.

Budowa formularza

Wraz z wprowadzeniem klasy Zend_Form_Element_File wysyłanie sprowadza się do umieszczenia tego elementu w formularzu a następnie wywołaniu metody receive() w celu odebrania pliku.Oto najprostszy przykład formularza:

class UploadForm extends Zend_Form
{
    public function init()
    {
        $this->addElement('file', 'uploadFile', array(
            'destination' => APPLICATION_PATH.'/uploads',
            'validators' => array(
                array('count', false, 1),
                array('size', false, 102400),
            ),
            'label' => 'Wyślij plik:'
        ));

        $this->addElement('submit', 'submit', array(
            'label' => 'Wyślij'
        ));

        $this->setEnctype('multipart/form-data');
    }
}

Formularz zawiera tylko dwa pola: pole wyboru pliku oraz przycisk "wyślij". Podany przy pierwszym elemencie parametr destination określa ścieżkę dostępu do katalogu w którym znajdą się wysłane pliki. Katalog ten musi istnieć oraz mieć odpowiednie prawa dostępu - inaczej zamiast formularza zobaczymy komunikat błędu. Zwróćmy uwagę na specjalne walidatory - count określa maksymalną liczbę wysyłanych plików, a size - dopuszczalny rozmiar (w bajtach). Oprócz nich możemy posłużyć się innymi, na przykład:

  • NotExists - zapobiega nadpisaniu pliku przy wysyłaniu;
  • Extension - dopuszcza wyłącznie pliki z podanym rozszerzeniem;
  • IsImage - akceptuje wyłącznie obrazy.

Wybór jest jeszcze większy - lista z dokładnymi opisami znajduje się w manualu.

Odbieranie pliku

Naciśnięcie przycisku "Wyślij" spowoduje wysłanie wybranego pliku razem z formularzem. Odbywa się to synchronicznie, co oznacza że strona jest zablokowana dopóki przeglądarka nie zakończy transferu. Nie jes to zbyt wygodne w przypadku większych plików. Użytkownik powinien być przynajmniej poinformowany o tym że coś się dzieje i nie należy zamykać przeglądarki. A jeszcze lepiej gdy zobaczy, pokazujący jaka część pliku jest już na serwerze. Te zagadnienia zostaną opisane w następnych artykułach.
Zwykłe odbieranie pliku może wyglądać tak:

$uploadForm = new UploadForm();

if ($request->isPost()) {

    if (!$uploadForm->isValid($request->getPost())) { // Próba walidacji formularza
        $information = 'Błąd podczas sprawdzania poprawności formularza.';
    } elseif (!$uploadForm->uploadFile->receive()) { // Odbiór pliku
        $information = 'Błąd podczas odbierania pliku.';
    } else { // Sukces
        $information = 'Plik ' . $uploadForm->uploadFile->getFileName()
                     . ' został poprawnie wysłany.';
    }

}

Co robi ten kod? Po pierwsze sprawdza czy formularz został wysłany metodą POST. Jeśli tak to badamy jego poprawność (isValid()).  Gdy plik spełnia wymagania walidatorów, możemy spróbować go odebrać (receive()). Jeżeli ta metoda zwróci wartość true, możemy mieć pewność że plik jest już na serwerze. Jego lokalizację określa parametr destination.

Samo przesłanie pliku może odbyć się też "nie wprost". Plik zostanie przesłany automatycznie przy wywołaniu metody getValues(). Nie wiem czy jest to najlepsze rozwiązanie - może sprawić problemy w zrozumieniu kodu.

Zend_Form_Element_File udostępnia też kilka pomocniczych, informacyjnych metod: isUploaded(), isReceived(), isFiltered(). Mogą pomóc rozpoznać czy plik został w ogóle wysłany i co się z nim aktualnie dzieje.

Poinformujmy użytkownika że nie wybrał żadnego pliku:

if (!$uploadForm->isValid($request->getPost())) { // Próba walidacji formularza
        $information = 'Błąd podczas sprawdzania poprawności formularza.';
    } elseif (!$uploadForm->uploadFile->isUploaded()) { // Czy cokolwiek zostało wysłane?
        $information = 'Nie wybrano pliku do wysłania.';
    } elseif (!$uploadForm->uploadFile->receive()) { // Odbiór pliku
        $information = 'Błąd podczas odbierania pliku.';
    } else { // Sukces
        $information = 'Plik ' . $uploadForm->uploadFile->getFileName()
                     . ' został poprawnie wysłany.';
    }
}

Widok

Skrypt widoku zawiera formularz i pole informacyjne:

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Wysyłanie plików przy użyciu Zend_Form - Przykład 1</title>
</head>

<body>

<div id="wrapper">
    <h1>Wysyłanie plików przy użyciu Zend_Form - Przykład 1</h1>

    <div id="information">
        <?= $this->information ?>
    </div>

    <?= $this->uploadForm ?>

</div>

</body>
</html>

Podsumowanie

Ten prosty kod wystarczy do synchronicznego, blokującego przeglądarkę wysyłania plików. Metoda sprawdza się gdy rozmiar zbioru jest dosyć mały i cały proces trwa szybko. Jednak pisząc poważną aplikację musimy być bardziej elastyczni - w następnym artykule pokarzę jak zbudować formularz wysyłający pliki w tle.

Przykład

Przykładowa aplikacja została zbudowana na bazie kilku komponentów Zenda: Zend_View, Zend_Controller_Request, Zend_Form. Nie korzysta z architektury MVC. Nie jest zależna od mod_rewrite. Aby uruchomić ją wystarczy przenieść pliki do wybranego katalogu i ustawić poprawną ścieżkę dostępu do bibliotek Zenda w pliku index.php (set_include_path()).

Pobierz przykładowe pliki.

Przydatne linki

Więcej na ten temat:


comments powered by Disqus