Chris Tankersley

Uploading Files using Zend Framework

Posted on 2010-01-07

Uploading files in PHP is easy, if not tedious. A lot of the projects that I work on involve some type of upload of files and after a while it gets old. Doing all the checks to make sure the file is there, generating forms, etc ... there must be an easier way, right?

Well, there is. The Zend Framework introduced a few classes to help facilitate uploading and working with files after they make it to the server. Taking advantage of Zend_Form_Element_File and the Zend_File_Transfer_Adapter_Http makes it dead simple to upload a file.

Generating a Form

The first step is to generate a form. I use Zend_Form more and more for this as it takes away having to do the markup for forms as well as provides an easy way to mark up the forms. Let's make a simple upload form:

    class Form_MyForm extends Zend_Form {
      public function init() {
        $file = new Zend_Form_Element_File('file');
        $file->setLabel('File to Upload:')->setRequired(true);

        $submit = new Zend_Form_Element_Submit('submit');
        $submit->setLabel('Upload File');
        $this->assignElements(array($file, $submit));
      }
    }

Zend_Form 1.9.6 takes care of making sure that the form itself is set to to the proper encoding type for you and making sure that a MAX_FILESIZE field is set up. Less work you have to do! Now you just display that form in your view.

Getting the File

Now that we're allowing the user to upload the file, how do we get it? You can go back to using the $_FILES superglobal and the upload functions but there is an easier way. Let's take a look.

// Check for Post and Validate your form before this

// Define a transport and set the destination on the server
$upload = new Zend_File_Transfer_Adapter_Http();
$upload->setDestination(APPLICATION_PATH.'/../data/');

try {
// This takes care of the moving and making sure the file is there
$upload->receive();
// Dump out all the file info
Zend_Debug::dump($upload->getFileInfo());
} catch (Zend_File_Transfer_Exception $e) {
echo $e->message();
}

So with 8 lines of code we have our error checking and getting the file moved into the proper place. It does all the is_uploaded_file, move_uploaded_file, making sure it gets there, and renaming the file. That's it!

But wait, I don't want the filename to be whatever the original filename was!

You're covered. Zend_Filter_File_Rename allows you to rename a file however you want when you receive the file. Let's say you allow the user to upload a new avatar picture for a Forum, and that file is always called '$userId.jpg'.

$upload = new Zend_File_Transfer_Adapter_Http();
$upload->addFilter('Rename', APPLICATION_PATH.'/../public/images/avatars/'.$userId.'.jpg');

That's it. When the file is received (and that is the important part which I'll show in a minute) it will be renamed and put in the appropriate folder. Notice I didn't set a destination - when using the Rename filter, the destination is part of the filter.

Caveats

Receiving a file, and Zend_Form::getValues()

Always make sure to receive your file with the receive() method before calling the getValues() method on your form. getValues() will internally try and shuffle your file around since Zend_Form is aware of the file upload and receive() won't work. It is an easy but frustrating mistake to make.

Renaming and Receiving a file

Try the following:

$upload = new Zend_File_Transfer_Adapter_Http();
$upload->addFilter('Rename', APPLICATION_PATH.'/../public/images/avatars/'.$userId.'.jpg');
Zend_Debug::dump($upload->getFileInfo());
$upload->receive();
Zend_Debug::dump($upload->getFileInfo());

You'll notice that the name of the file doesn't get rewritten until after the receive. This is because the file is already on the server by the time your code runs, the Transport just makes it easier to work with. It is important to remember this if you need the original filename but don't want that to be the name on the local filesystem. Once the Rename filter is run the original filename is lost.

For example, in one internal project for myself I upload the files and hash the name on the filesystem, but in the database I need to have the original filename when the user downloads the file. In that case you can call all the Zend_File_Transfer_Adapter_Http() methods before the rename, receive the file, and then work with the file after that.

Going Futher

The manual for Zend_File has more filters and examples for the uploaded files. You can even have your form support multiple file upload elements with minor alterations to the examples above. There are validators specifically for files to make the file uploads safer (not foolproof, but a bit safter)

Take advantage of Zend Framework's tight integration for forms and file uploads and make your life easier.


Comments