Reputation: 238937
I'm creating a web application that will involve habitual file uploading and retrieval (PDFs, Word Documents, etc).
Here are the requirements:
Question #1: Where should I store these files?
On the file system?
uploads
directory in my public
directory?2009-12-01
) and create a sym link to it titled current
. This would mean that the uploads
directory would need to be outside of this directory so it could be shared among all the different versions of the application, regardless of which one is being used at the time.In the database?
What do you think I should do?
Here's what I ended up doing:
I deploy a new version of my application, and create a symlink to the version I would like to use:
/server/myapp/releases/2009-12-15
/server/myapp/current -> /server/myapp/releases/2009-12-15
I created a directory above my application directory, and a symlink to that directory in my application:
/server/uploads
/server/myapp/current/application/data/uploads -> /server/uploads
I defined an APPLICATION_UPLOADS_DIR
variable in my /public/index.php
file:
// Define path to uploads directory
defined('APPLICATION_UPLOADS_DIR')
|| define('APPLICATION_UPLOADS_DIR', realpath(dirname(__FILE__) . '/../data/uploads'));
And used this variable in my upload form to set the directory to upload to:
<?php
class Default_Form_UploadFile extends Zend_Form
{
public function init()
{
//excerpt
$file = new Zend_Form_Element_File('file');
$file->setLabel('File to upload:')
->setRequired(true)
->setDestination(APPLICATION_UPLOADS_DIR);
}
}
In my controller, I rename the file to something unique so it can't be overwritten by a file with the same name. I save the unique filename, original filename, and mime-type in the database so I can use it later:
public function uploadAction()
{
//...excerpt...form has already been validated
$originalFilename = pathinfo($form->file->getFileName());
$newFilename = 'file-' . uniqid() . '.' . $originalFilename['extension'];
$form->file->addFilter('Rename', $newFilename);
try {
$form->file->receive();
//upload complete!
$file = new Default_Model_File();
$file->setDisplayFilename($originalFilename['basename'])
->setActualFilename($newFilename)
->setMimeType($form->file->getMimeType())
->setDescription($form->description->getValue());
$file->save();
return $this->_helper->FlashMessenger(array('success'=>'The file has been uploaded.'));
} catch (Exception $exception) {
//error
}
}
To download the files, I created a download action which retrieves the file and sends it to the user with the help of Noginn's SendFile Action Helper.
public function downloadAction()
{
//user already has permission to download the file
$this->_helper->layout()->disableLayout(); //won't work if you don't do this
$file = $this->_getFileFromRequest();
$location = APPLICATION_UPLOADS_DIR . '/' . $file->getActualFilename();
$mimeType = $file->getMimeType();
$filename = $file->getDisplayFilename();
$this->_helper->sendFile($location, $mimeType, array(
'disposition' => 'attachment',
'filename' => $filename
));
}
To offer a download link in the view script, I point them to the download action with the file id param:
<a href="<?php echo $this->url(array(
'controller'=>'files',
'action'=>'download',
'file'=>$file->id)); ?>"><?php echo $file->displayFilename; ?></a>
That's it! If you have any other advice or criticisms of this method, please post your answers/comments.
Upvotes: 3
Views: 2875
Reputation: 11052
You don't have to store it in you app tree - you can store it elsewhere and configure your web server to point /uploads virtual dir to that place (Alias in Apache, Virtual directory in IIS - most webservers have it), and have the application link to /uploads. You can of course use symlinks too. Having separate directory would also allow you to share it in case you would have more than one server running the same application.
Upvotes: 1
Reputation: 1319
Symlinking to a directory outside web root as Pascal suggested is a good solution. One way to do it, especially if you need some kind of user access control over the files, would be to use a php script to serve those files. You could have a download controller/action etc. that takes the file name as a parameter, then loads the file from some directory outside web root and outputs it. Then you would also need to send some headers before the file itself so the browser knows what to do with it. Headers could be set file type specific or just the generic "octet-stream" to force browser to show the download dialog.
Upvotes: 1
Reputation: 401142
To allow users to diretly download files, two solutions :
Alias
would do, too, actually -- the example in Apache's manual looks a bit like your situation ^^
About your second point with versionned directories : what I often do is :
/var/www
for instance) ; say /var/media
/var/www/media
points to /var/media
ln -s /var/media /var/www/media
This works nice, is easy to deploy, and "tricks" the application into thinking the files are in one of its sub-directories -- even if it's not physicaly the case.
Upvotes: 2