File uploads with Zend_Form_Element_File

Now that Zend Framework 1.7 has been released, I thought I'd take a look at the built in file upload element, Zend_Form_Element_File, and see how it can be used. This is how to use it in its most basic form.

I decided to use the same set of form elements as before in order to make things easy.

Zend_Form_Element_File_Example.png

Let's start with the form:

The form

We extend Zend_Form and store it in the application/forms folder and so the class name is forms_UploadForm:

<?php

class forms_UploadForm extends Zend_Form
{
    public function __construct($options null)
    {
        parent::__construct($options);
        $this->setName('upload');
        $this->setAttrib('enctype''multipart/form-data');

        $description = new Zend_Form_Element_Text('description');
        $description->setLabel('Description')
                  ->setRequired(true)
                  ->addValidator('NotEmpty');

        $file = new Zend_Form_Element_File('file');
        $file->setLabel('File')
            ->setDestination(BASE_PATH '/data/uploads')
            ->setRequired(true);

        $submit = new Zend_Form_Element_Submit('submit');
        $submit->setLabel('Upload');

        $this->addElements(array($description$file$submit));

    }
}

As before, we set the name and enctype attribute of the form to allow for files to be uploaded. The form itself has two fields: a text field called 'description' and the file upload field called 'file', along with a submit button. Nothing especially complicated here.

The Zend_Form_Element_File element has a setDestination() method which is used to tell the underlying Zend_File_Transfer_Adapter_Http where we want the file that is uploaded to be stored. In this case we choose data/uploads.

The controller & view

The controller is also very standard:

<?php

class IndexController extends Zend_Controller_Action 
{
    public function indexAction() 
    {
        $this->view->headTitle('Home');
        $this->view->title 'Zend_Form_Element_File Example';
        $this->view->bodyCopy "<p>Please fill out this form.</p>";

        $form = new forms_UploadForm();

        if ($this->_request->isPost()) {
            $formData $this->_request->getPost();
            if ($form->isValid($formData)) {

                // success - do something with the uploaded file
                $uploadedData $form->getValues();
                $fullFilePath $form->file->getFileName();

                Zend_Debug::dump($uploadedData'$uploadedData');
                Zend_Debug::dump($fullFilePath'$fullFilePath');

                echo "done";
                exit;

            } else {
                $form->populate($formData);
            }
        }

        $this->view->form $form;

    }
}

The view, views/scripts/index.phtml, is trivial:


<h1><?= $this->title?></h1>
<?= $this->bodyCopy?>
<?= $this->form?>

If the form validates correctly, the $uploadedData array will contain the values of the form fields along with the filename of the file that was uploaded. Note that this filename is not fully qualified, so if you need the entire path to the file, then use the getFileName() method on the file element.

Conclusion

That's all there is to it for simple uploading of forms. There are still a few fairly important bugs in the component that we'll have to wait for 1.7.2 for. Specifically the Count validator doesn't always work as you'd expect and don't use getValues() and receive() as it isn't yet clever enough to know not to call move_uploaded_file() more than once.

As usual, here's a zip file of the project I created to test this: Zend_Form_Element_File_Example.zip (including Zend Framework (a snapshot of the release-1.7 branch) which is why it's 3.9MB large).

19 Responses to “File uploads with Zend_Form_Element_File”

  1. 1 John Walter

    For Zend_Form you can use the "init"-function. Look at the quick start tutorial: http://framework.zend.com/docs/quickstart/create-a-form

  2. 2 Rob...

    John,

    Yep - init() in place of using the standard constructor works fine.

    Regards,

    Rob...

  3. 3 Gonzalo

    Hi Rob my name is gonzalo benitez i have a question.. i am developing a multilanguage website, and i had choosen the array form notation to display the options like filter, validators, labes etc.- because it seemed faster and better to me. The problem is that is more dificult to translate with poedit because the strings. What do you think about it?

  4. 4 Rob...

    Gonzalo,

    I think you'll get a better answer on the i18n mailing lists where there are people who understand translation issues much better than I do!

    Regards,

    Rob...

  5. 5 Gonzalo

    Thanks anyway! great example cheers

  6. 6 Fabrizio

    Hi Rob, thank you for sharing. (I found the bug about count validator too)

    Maybe I'm wrong but it seems to be not possible use 'setErrorMessages()' as method of Zend_Form_Element_File as usually used for remaining fields. There's any workaround for this?

    Regards,
    Fabrizio

  7. 7 thomas

    You should also have mentioned that the implementation of Zend_Form_Element_File is delivered with many validators and some filters which allow much more flexibility than a simple upload.

    For your information: getValues() does not return the full path for security reasons.

    Btw: The multiple receivement bug was fixed on the same day you reported it.

    Greetings,
    Thomas ( the one who made this component :-) )

  8. 8 Rob...

    Hi Thomas,

    Yeah - there are many validators available for Zend_Form_Element_File, but I didn't want to complicate the basic usage :)

    I was glad to see the multiple receive() issue solved so quickly, but it's a shame it missed 1.7.1. I should have played with this stuff a week or so earlier!

    Regards,

    Rob...

  9. 9 Tim Reynolds

    Nice post. Thank you for the info. Keep it up.

  10. 10 Sebastian

    Doesn't this cause race conditions when multiple users concurrently upload a file named a.ext?

  11. 11 Rob...

    Sebastian,

    Probably :)

    Rob...

  12. 12 thomas

    But of course only when they upload the same file within the same millisecond. :-)

    And that's a rare race condition (you would get an error/exception as user in this case)

    Greetings
    Thomas

  13. 13 Zach

    I'm working on a project using Zend_Form_Element_File myself right now, and I'm interested in how you'd handle a situation where you need to rename the file based on a database quey (lastinsertid.) Its pretty obvious that this element is fairly new, because its really a major security flaw to not have anything in place to easily rename the uploaded file based on critera like a userid or a pictureid. You can set it to not overwrite an existing file, at least closing the hole of your admin's profile getting overwritten with goatse, but that also makes it quite hard to upload generic sounding files (how many people might name a file profile.jpg? I'm guessing more than one on the entire internet) or even impossible in a situation where you're collecting the same exact file from people ("Upload config.ini here please.") Right now the only way I can see to do this cleanly would be to massively extend the file element, and have my own functions for recieve etc in my class that take an id or something. The only other thing I could think of was do an if(isuploaded) and then go in and setdestination() after doing whatever needs to be done to make it a unique filename.

  14. 14 thomas

    @Zach:
    Don't you think that this is quite unhandy ?

    Wouldn't it be better to use the Rename Filter instead of extending the form element ? ;-)

    Greetings
    Thomas

  15. 15 Zach

    Its kind of a chicken and the egg thing, unless I'm missing something though. I'm really new to Zend Framework so I'd love if you could correct me, but I want to rename the file with a unique id number, but I can only create that unique ID number after the file has uploaded and and the move filter has been triggered?

    Or could I set the filter after isuploaded() but before receive()?

  16. 16 thomas

    Simplified:

    if (isUploaded) {
    $adapter->addFilter('Rename', $myownname);
    if (isValid) {
    $adapter->receive();
    } else {
    print "Oops";
    }
    }

    Greetings
    Thomas

  17. 17 Zach

    Thanks!

  18. 18 Flavio

    I used Zend_Form_Element_File to upload a file to webserver through a Zend_form class. How could I use the same class to download the file uploaded previously? I would appreciate any help.
    Thank you,
    Flávio.

  19. 19 daniel

    Why still Zend_form_element_file has wrong name when is nested in subform? It is because Multi-Page forms are not officially supported in Zend_Form?

    This problem was closed, please take a look
    http://framework.zend.com/issues/browse/ZF-5835

    Please Thomas help me.

The views expressed in these comments are not the views of the publisher. However, we believe in the rights of others to express their legitimate views and concerns. Any legitimate complaint emailed to rob@akrabat.com will be seriously considered and the post reviewed as desirable and necessary.

Leave a Reply

Buy now!