Friday
17
April
2009

Recieving files with Zend_Form_Element_File

How to receive a file from Zend_Form_Element_File is often a difficult question.
When you have problems you should read below to get an idea of the handling and to prevent possible problems:

Let’s expect our form has a file element like this one:

$element = new Zend_Form_Element_File('file');
$element->setDestination('/MyDir');

In past, before ZF 1.8, when you wanted to get the uploaded file you had to call receive() afterwards like this…

if ($form->isValid($this->getPost())) {
    if (!$element->file->receive()) {
        print_r($element->file->getMessages());
    }
}

Now many people said, and I think they were right, that it would be a great idea to get the file when calling getValues(). So we added this new feature for ZF 1.8.

This means that that with ZF 1.8 this call is enough:

$values = $form->getValues();

Your file is already transferred… no need to call received afterwards.

But sometime it is wished to call received manually. You could, for example, wish to add a own filter based on a value which you get from your form. So calling getValues() now would mean that you can not add a filter afterwards… as the file is already transferred.

But also for this usecase a solution has been added. You can set a flag to the file element which prevents that the file itself will be received when you call getValues().

Take a look at the following file element:

$element = new Zend_Form_Element_File('file');
$element->setDestination('/MyDir')
        ->setValueDisabled(true);

Note the added call of setValueDiabled(). It tells the file element that it should not receive the file when you call getValues().

Now you can call getValues() and the file will not be received in the background. But this means, of course, that you have to call receive() manually. Otherwise your file will not be available after the script has been finished.

$values = $form->getValues();
	
if ($form->isValid($this->getPost())) {
    $element->file->addFilter('Rename', false, 'Filename_'.$values['id']);
    if (!$element->file->receive()) {
        print_r($element->file->getMessages());
    }
}

Remember, to have this working you need to call setValueDisabled() otherwise the file is transferred when calling getValues(). When you omit the call then your file will be transferred, due to getValues(), but it will not be renamed, because the rename filter was not available at the time, the file has been received.

Note, that using a filename based on a user input is always a security issue. Use a autoincremented number from your database for example, or be sure that you validate the user input before using it as new filename.

You should also note that I did not use another instance of Zend_File_Transfer…
The file element itself has already a instance available. When you really need to have the instance itself, then use getTransferAdapter() instead of creating a new instance. This is faster, uses less ressources and prevents possible problems with files already being transferred by the other instance.

Greetings
Thomas Weidner
I18N Team Leader, Zend Framework

Zend Framework Advisory Board Member
Zend Certified Engineer for Zend Framework

Back to top
  1. Peter

    Friday, April 24, 2009 - 11:13:35

    If running you last code example, calling $element->file->getFileName() after $element->file->receive(),
    shouldn’t that return /MyDir/Filename_1? Actually it returns /MyDir/filename_of_uploaded_file

  2. thomasw

    Friday, April 24, 2009 - 13:23:38

    getFileName() returns the actual name of the file…
    This means, before setting the filter it returns the original name, and after setting the filter it returns the filtered name

  3. Peter

    Friday, April 24, 2009 - 14:19:48

    Nope, it does not (at least not in 1.8 beta1).

    In Zend_File_Transfer_Adapter_Http::receive() the filtered $filename is set to $this->_files[$file][’tmp_name’]; while Zend_File_Transfer_Adapter_Abstract::getFileName() looks at $this->_files[$file][’name’] for the filename.

  4. thomasw

    Friday, April 24, 2009 - 15:15:37

    receive() receives the file… of course when it does this, the internal name changes… tmp_name is the internal temporary filename… but this has nothing to do with the filename the user gave.

    Btw: When you see a problem with ZF code please fill in a issue instead of discussing here single lines of code. A blog is not the proper media to get help on code which is not part of the blog entry.

  5. Peter

    Friday, April 24, 2009 - 15:22:56

    Sure, i was just wondering if theres a method to return the actual path of the transfered file after receive().

  6. Peter

    Friday, April 24, 2009 - 17:13:25

    Ah, you fixed that in trunk…that was fast, thanks :)

  7. Cristian

    Monday, May 4, 2009 - 14:33:49

    There is any possibility to automatically process some filters also ? Not just put the file into destination folder….

  8. thomasw

    Monday, May 4, 2009 - 21:42:00

    Just read the above blog entry in detail.
    As you can see there is a addFilter() method within the file element.

  9. Cristian

    Friday, May 8, 2009 - 23:58:32

    Actually i was curious if would be possible to automatically use filters (as rename) automatically, with setValueDisabled not been set…

  10. thomasw

    Monday, May 11, 2009 - 23:04:02

    I don’t understand your question.
    setValueDisabled is per default set to false. This has nothing to do with filters per se.

  11. Tomek

    Tuesday, May 19, 2009 - 19:08:27

    I think there is a mistake in the examples: $form doesn’t have a method getPost()

  12. Vladimir

    Saturday, August 1, 2009 - 21:55:32

    $element izn`t available.
    I use
    $element = $form->getElement(’textfile’);
    $element->addFilter(’Rename’, array(false, ‘Filename_’.$i.’.txt’));

  13. Vladimir

    Saturday, August 1, 2009 - 22:11:04

    $element isn`t available.
    I use
    $element = $form->getElement(’textfile’);
    $element->addFilter(’Rename’, array(false, ‘Filename_’.$i.’.txt’));
    But it doesn’t work

  14. Vladimir

    Tuesday, August 4, 2009 - 10:36:40

    I’m sorry. It works.
    $element->addFilter(’Rename’, ‘Filename_’.$i.’.txt’);

  15. thomasw

    Tuesday, August 4, 2009 - 11:03:03

    @Tomek:
    No one said that the form has a isPost() method. You may note the $this and not the $form variable, which implies that the controller and not the form has this method.

    @Vladimir:
    When you supress the array keys you need to have them in the right order, otherwise you may notice unexpected behaviour.

  16. Vladimir

    Tuesday, August 4, 2009 - 15:54:05

    thank
    I can’t disable file auto uploading if I call $form->getValues(). In file with form i set
    $this->getElement(’imagefile’)->setValueDisabled(true);

    in controller $form->getElement(’textfile’)->setValueDisabled(true);

    $model = new Default_Model_Guestbook($form->getValues());
    $id = $model->save();
    //get last inserted id
    //move files
    $element = $form->textfile;
    $element->addFilter(’Rename’, ‘file’.$id.’.txt’);
    if (!$element->receive()) {
    print_r($element->getMessages());exit();
    }

    file isn’t renamed. If i use before
    $form->getElement(’textfile’)->setValueDisabled(true);
    // Since we now know the form validated, we can now
    // start integrating that data sumitted via the form
    // into our model:
    //print_r($form->getValues());exit();
    $model = new Default_Model_Guestbook($form->getValues());
    $id = $model->save();
    //get last inserted id
    //move files
    $element = $form->textfile;
    $element->addFilter(’Rename’, ‘file’.$id.’.txt’);
    if (!$element->receive()) {
    print_r($element->getMessages());exit();
    }
    $model = new Default_Model_Guestbook($form->getValues());
    $id = $model->save();

    it works, but i don’t know inserted id

  17. Vladimir

    Tuesday, August 4, 2009 - 15:56:55

    thank
    previous comment is wrong
    I can’t disable file auto uploading if I call $form->getValues(). In file with form i set
    $this->getElement(’imagefile’)->setValueDisabled(true);

    in controller $form->getElement(’textfile’)->setValueDisabled(true);

    $model = new Default_Model_Guestbook($form->getValues());
    $id = $model->save();
    //get last inserted id
    //move files
    $element = $form->textfile;
    $element->addFilter(’Rename’, ‘file’.$id.’.txt’);
    if (!$element->receive()) {
    print_r($element->getMessages());exit();
    }

    file isn’t renamed. If i use before

    $element = $form->textfile;
    $element->addFilter(’Rename’, ‘file’.$id.’.txt’);
    if (!$element->receive()) {
    print_r($element->getMessages());exit();
    }
    $model = new Default_Model_Guestbook($form->getValues());
    $id = $model->save();

    it works, but i don’t know inserted id

  18. Deitrich

    Sunday, October 11, 2009 - 10:43:38

    Thanks for the post, Thomas.

    This post clears up some confusion I have seen on other posts. However, you do not state where the various blocks of code are to be placed. Does some of this code need to be placed in an extension of Zend_Form while the rest of the code needs to be placed in a controller class?

    I suspect that just the first code block needs to be in a custom Zend_Form class and the rest is in a controller class.

  19. thomasw

    Sunday, October 11, 2009 - 12:29:13

    I did not mention where to place this code because you should already know this when working with Zend_Form and their elements.

    Take a look into the manual for Zend_Form to see how and where to place form elements, and where to validate them. There are several ways which could be used. Within your model, within the controller or even directly. It depends on your application.