views:

261

answers:

2

I am using Zend Framework 1.9.6. I think I've got it pretty much figured out except for the end. This is what I have so far:

Form:

<?php

class Default_Form_UploadFile extends Zend_Form
{
    public function init()
    {
        $this->setAttrib('enctype', 'multipart/form-data');
        $this->setMethod('post');

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

        $file = new Zend_Form_Element_File('file');
        $file->setLabel('File to upload:')
            ->setRequired(true)
            ->addValidator('NotEmpty')
            ->addValidator('Count', false, 1);
        $this->addElement($file);

        $this->addElement('submit', 'submit', array(
            'label'    => 'Upload',
            'ignore'   => true
        ));
    }
}

Controller:

public function uploadfileAction()
{
    $form = new Default_Form_UploadFile();
    $form->setAction($this->view->url());

    $request = $this->getRequest();

    if (!$request->isPost()) {
        $this->view->form = $form;
        return;
    }

    if (!$form->isValid($request->getPost())) {
        $this->view->form = $form;
        return;
    }

    try {
        $form->file->receive();
        //upload complete!
        //...what now?
        $location = $form->file->getFileName();
        var_dump($form->file->getFileInfo());
    } catch (Exception $exception) {
        //error uploading file
        $this->view->form = $form;
    }
}

Now what do I do with the file? It has been uploaded to my /tmp directory by default. Obviously that's not where I want to keep it. I want users of my application to be able to download it. So, I'm thinking that means I need to move the uploaded file to the public directory of my application and store the file name in the database so I can display it as a url.

Or set this as the upload directory in the first place (though I was running into errors while trying to do that earlier).

Have you worked with uploaded files before? What is the next step I should take?

Solution:

I decided to put the uploaded files into /data/uploads (which is a sym link to a directory outside of my application, in order to make it accessible to all versions of my application).

# /public/index.php
# Define path to uploads directory
defined('APPLICATION_UPLOADS_DIR')
    || define('APPLICATION_UPLOADS_DIR', realpath(dirname(__FILE__) . '/../data/uploads'));

# /application/forms/UploadFile.php
# Set the file destination on the element in the form
$file = new Zend_Form_Element_File('file');
$file->setDestination(APPLICATION_UPLOADS_DIR);

# /application/controllers/MyController.php
# After the form has been validated...
# Rename the file to something unique so it cannot be overwritten with a file of the same name
$originalFilename = pathinfo($form->file->getFileName());
$newFilename = 'file-' . uniqid() . '.' . $originalFilename['extension'];
$form->file->addFilter('Rename', $newFilename);

try {
    $form->file->receive();
    //upload complete!

    # Save a display filename (the original) and the actual filename, so it can be retrieved later
    $file = new Default_Model_File();
    $file->setDisplayFilename($originalFilename['basename'])
        ->setActualFilename($newFilename)
        ->setMimeType($form->file->getMimeType())
        ->setDescription($form->description->getValue());
    $file->save();
} catch (Exception $e) {
    //error
}
+3  A: 

By default, files are uploaded to the system temporary directory, which means you'll to either :

  • use move_uploaded_file to move the files somewhere else,
  • or configure the directory to which Zend Framework should move the files ; your form element should have a setDestination method that can be used for that.

For the second point, there is an example in the manual :

$element = new Zend_Form_Element_File('foo');
$element->setLabel('Upload an image:')
        ->setDestination('/var/www/upload')
        ->setValueDisabled(true);

(But read that page : there are other usefull informations)

Pascal MARTIN
+1 For configuring the destination in the form element and by pointing the manual.
Luiz Damim
+1  A: 

If you were to move the file to a public directory, anyone would be able to send a link to that file to anyone else and you have no control over who has access to the file.

Instead, you could store the file in the DB as a longblob and then use the Zend Framework to provide users access the file through a controller/action. This would let you wrap your own authentication and user permission logic around access to the files.

You'll need to get the file from the /tmp directory in order to save it to the db:

// I think you get the file name and path like this:
$data = $form->getValues(); // this makes it so you don't have to call receive()
$fileName = $data->file->tmp_name; // includes path
$file = file_get_contents($fileName);

// now save it to the database. you can get the mime type and other
// data about the file from $data->file. Debug or dump $data to see
// what else is in there

Your action in the controller for viewing would have your authorization logic and then load the row from the db:

// is user allowed to continue?
if (!AuthenticationUtil::isAllowed()) {
   $this->_redirect("/error");
}

// load from db
$fileRow = FileUtil::getFileFromDb($id); // don't know what your db implementation is

$this->view->fileName = $fileRow->name;
$this->view->fileNameSuffix = $fileRow->suffix;
$this->view->fileMimeType = $fileRow->mime_type;
$this->view->file = $fileRow->file;

Then in the view:

<?php
header("Content-Disposition: attachment; filename=".$this->fileName.".".$this->fileNameSuffix);
header('Content-type: ".$this->fileMimeType."');
echo $this->file;
?>
Chris Williams
Thanks for talking about what to do once the user needs to download the file. I will be implementing a download action like you mentioned.
Andrew