File Upload Component w/ Automagic - Model optional
I've written this FileUpload component which provides a lot of automagic to the file uploading process. The problem with all the other File Uploading helpers is they almost always require some sort of database/model for them, which really should be optional when all you want is an easy way to upload files from your website. FileUpload is a component that takes little to zero configuration, detects when a file is being uploaded, verifies its type is allowed, moves the file to a specified folder (being careful never to overwrite anything), and if there is a Model set and present will automatically create a record of it in your database. I couldn't find a simpler "does-it-all-for-you" component for uploading images/files so I wrote one myself. I hope you find it useful.
NOTE: This stand alone component is no longer supported. This component has been updated and repackaged into a full featured file upload plugin that is available from github. It is highly recommend that you upgrade your component to the FileUpload plugin.
Source: http://github.com/webtechnick/CakePHP-FileUpload-Plugin
The FileUpload Component:
app/controllers/components/file_upload.php
You can use this Component with or without a model. It defaults to use the Upload model:
If you wish to NOT use a model simply set $this->FileUpload->fileModel = null; in a beforeFilter.
If you're using a Model, you'll need to have at least 3 fields to hold the uploaded data (name, type, size)
Example Table:
Default fields are name, type, and size; but you can change that at anytime using the $this->FileUpload->fields = array();
Depending on whether or not you are using a model your view should hold a file input type with the name of your fileVar.
Example View WITH Model:
Example View WITHOUT a Model:
Upon submitting a file the FileUpload Component will automatically search for your uploaded file, verify its of the proper type set by $this->FileUpload->allowedTypes:
Then it will attempt to copy the file to your uploads directory set by $this->FileUpload->upload_dir:
If a fileModel is given, it will attempt to save the record of the uploaded file to the database for later use. Upon success the FileComponent sets $this->FileUpload->success to TRUE; You can use this variable to test in your controller like so:
To View the photo variable you might type something like
At any time you can remove a file by using the $this->FileUpload->removeFile($name); function. An example of that being used might be in a controller:
Simple as that. Automagic File Uploading. I hope you enjoy it. If you read through the documentation I've written in the actual FileUpload Component it will give you detailed examples and explanations of each variable/function. Comments are appreciated.
Source: http://github.com/webtechnick/CakePHP-FileUpload-Plugin
The FileUpload Component:
app/controllers/components/file_upload.php
Controller Class:
<?php
/***************************************************
* FileUpload Component
*
* Manages uploaded files to be saved to the file system.
*
* @copyright Copyright 2009, Webtechnick
* @link http://www.webtechnick.com
* @author Nick Baker
* @version 1.4
* @license MIT
*/
class FileUploadComponent extends Object{
/***************************************************
* fileModel is the name of the model used if we want to
* keep records of uploads in a database.
*
* if you don't wish to use a database, simply set this to null
* $this->FileUpload->fileModel = null;
*
* @var mixed
* @access public
*/
var $fileModel = 'Upload';
/***************************************************
* uploadDir is the directory name in the webroot that you want
* the uploaded files saved to. default: files which means
* webroot/files must exist and set to chmod 777
*
* @var string
* @access public
*/
var $uploadDir = 'files';
/***************************************************
* fileVar is the name of the key to look in for an uploaded file
* For this to work you will need to use the
* $form-input('file', array('type'=>'file));
*
* If you are NOT using a model the input must be just the name of the fileVar
* input type='file' name='file'
*
* @var string
* @access public
*/
var $fileVar = 'file';
/***************************************************
* allowedTypes is the allowed types of files that will be saved
* to the filesystem. You can change it at anytime without
* $this->FileUpload->allowedTypes = array('text/plain',etc...);
*
* @var array
* @access public
*/
var $allowedTypes = array(
'image/jpeg',
'image/gif',
'image/png',
'image/pjpeg',
'image/x-png'
);
/***************************************************
* fields are the fields relating to the database columns
*
* @var array
* @access public
*/
var $fields = array('name'=>'name','type'=>'type','size'=>'size');
/***************************************************
* This will be true if an upload is detected even
* if it can't be processed due to misconfiguration
*
* @var boolean
* @access public
*/
var $uploadDetected = false;
/***************************************************
* This will hold the uploadedFile array if there is one
*
* @var boolean|array
* @access public
*/
var $uploadedFile = false;
/***************************************************
* data and params are the controller data and params
*
* @var array
* @access public
*/
var $data = array();
var $params = array();
/***************************************************
* Final file is set on move_uploadedFile success.
* This is the file name of the final file that was uploaded
* to the uploadDir directory.
*
* @var string
* @access public
*/
var $finalFile = null;
/***************************************************
* success is set if we have a fileModel and there was a successful save
* or if we don't have a fileModel and there was a successful file uploaded.
*
* @var boolean
* @access public
*/
var $success = false;
/***************************************************
* errors holds any errors that occur as string values.
* this can be access to debug the FileUploadComponent
*
* @var array
* @access public
*/
var $errors = array();
/***************************************************
* Initializes FileUploadComponent for use in the controller
*
* @param object $controller A reference to the instantiating controller object
* @return void
* @access public
*/
function initialize(&$controller){
$this->data = $controller->data;
$this->params = $controller->params;
}
/***************************************************
* Main execution method. Handles file upload automatically upon detection and verification.
*
* @param object $controller A reference to the instantiating controller object
* @return void
* @access public
*/
function startup(&$controller){
$this->uploadDetected = ($this->_multiArrayKeyExists("tmp_name", $this->data) || $this->_multiArrayKeyExists("tmp_name",$this->data));
$this->uploadedFile = $this->_uploadedFileArray();
if($this->_checkFile() && $this->_checkType()){
$this->_processFile();
}
}
/*************************************************
* removeFile removes a specific file from the uploaded directory
*
* @param string $name A reference to the filename to delete from the uploadDirectory
* @return boolean
* @access public
*/
function removeFile($name = null){
if(!$name) return false;
$up_dir = WWW_ROOT . $this->uploadDir;
$target_path = $up_dir . DS . $name;
if(unlink($target_path)) return true;
else return false;
}
/*************************************************
* showErrors itterates through the errors array
* and returns a concatinated string of errors sepearated by
* the $sep
*
* @param string $sep A seperated defaults to <br />
* @return string
* @access public
*/
function showErrors($sep = "<br />"){
$retval = "";
foreach($this->errors as $error){
$retval .= "$error $sep";
}
return $retval;
}
/**************************************************
* _processFile takes the detected uploaded file and saves it to the
* uploadDir specified, it then sets success to true or false depending
* on the save success of the model (if there is a model). If there is no model
* success is meassured on the success of the file being saved to the uploadDir
*
* finalFile is also set upon success of an uploaded file to the uploadDir
*
* @return void
* @access private
*/
function _processFile(){
$up_dir = WWW_ROOT . $this->uploadDir;
$target_path = $up_dir . DS . $this->uploadedFile['name'];
$temp_path = substr($target_path, 0, strlen($target_path) - strlen($this->_ext())); //temp path without the ext
//make sure the file doesn't already exist, if it does, add an itteration to it
$i=1;
while(file_exists($target_path)){
$target_path = $temp_path . "-" . $i . $this->_ext();
$i++;
}
$save_data = array();
if(move_uploaded_file($this->uploadedFile['tmp_name'], $target_path)){
//Final File Name
$this->finalFile = basename($target_path);
$model =& $this->getModel();
$save_data[$this->fields['name']] = $this->finalFile;
$save_data[$this->fields['type']] = $this->uploadedFile['type'];
$save_data[$this->fields['size']] = $this->uploadedFile['size'];
if(!$model || $model->save($save_data)){
$this->success = true;
}
else{
$this->success = false;
}
}
else{
$this->_error('FileUpload::processFile() - Unable to save temp file to file system.');
}
}
/***************************************************
* Returns a reference to the model object specified, and attempts
* to load it if it is not found.
*
* @param string $name Model name (defaults to FileUpload::$fileModel)
* @return object A reference to a model object
* @access public
*/
function &getModel($name = null) {
$model = null;
if (!$name) {
$name = $this->fileModel;
}
if($name){
if (PHP5) {
$model = ClassRegistry::init($name);
} else {
$model =& ClassRegistry::init($name);
}
if (empty($model) && $this->fileModel) {
$this->_error('FileUpload::getModel() - Model is not set or could not be found');
return null;
}
}
return $model;
}
/***************************************************
* Adds error messages to the component
*
* @param string $text String of error message to save
* @return void
* @access protected
*/
function _error($text){
$message = __($text,true);
$this->errors[] = $message;
trigger_error($message,E_USER_WARNING);
}
/***************************************************
* Checks if the uploaded type is allowed defined in the allowedTypes
*
* @return boolean if type is accepted
* @access protected
*/
function _checkType(){
foreach($this->allowedTypes as $value){
if(strtolower($this->uploadedFile['type']) == strtolower($value)){
return true;
}
}
$this->_error("FileUpload::_checkType() {$this->uploadedFile['type']} is not in the allowedTypes array.");
return false;
}
/***************************************************
* Checks if there is a file uploaded
*
* @return void
* @access protected
*/
function _checkFile(){
if($this->uploadedFile && $this->uploadedFile['error'] == UPLOAD_ERR_OK ) return true;
else return false;
}
/***************************************************
* Returns the extension of the uploaded filename.
*
* @return string $extension A filename extension
* @access protected
*/
function _ext(){
return strrchr($this->uploadedFile['name'],".");
}
/***************************************************
* Returns an array of the uploaded file or false if there is not a file
*
* @param string $text String of error message to save
* @return array|boolean Array of uploaded file, or false if no file uploaded
* @access protected
*/
function _uploadedFileArray(){
if($this->fileModel){
$retval = isset($this->data[$this->fileModel][$this->fileVar]) ? $this->data[$this->fileModel][$this->fileVar] : false;
}
else {
$retval = isset($this->params['form'][$this->fileVar]) ? $this->params['form'][$this->fileVar] : false;
}
if($this->uploadDetected && $retval === false){
$this->_error("FileUpload: A file was detected, but was unable to be processed due to a misconfiguration of FileUpload. Current config -- fileModel:'{$this->fileModel}' fileVar:'{$this->fileVar}'");
}
return $retval;
}
/***************************************************
* Searches through the $haystack for a $key.
*
* @param string $needle String of key to search for in $haystack
* @param array $haystack Array of which to search for $needle
* @return boolean true if given key is in an array
* @access protected
*/
function _multiArrayKeyExists($needle, $haystack) {
if(is_array($haystack)){
foreach ($haystack as $key=>$value) {
if ($needle==$key) {
return true;
}
if (is_array($value)) {
if($this->_multiArrayKeyExists($needle, $value)){
return true;
}
}
}
}
return false;
}
}
?>
You can use this Component with or without a model. It defaults to use the Upload model:
Model Class:
<?php
class Upload extends AppModel{
var $name = 'Upload';
}
?>
If you wish to NOT use a model simply set $this->FileUpload->fileModel = null; in a beforeFilter.
Controller Class:
<?php
function beforeFilter(){
parent::beforeFilter();
$this->FileUpload->fileModel = null; //Upload by default.
}
?>
If you're using a Model, you'll need to have at least 3 fields to hold the uploaded data (name, type, size)
Example Table:
--
-- Table structure for table `uploads`
--
CREATE TABLE IF NOT EXISTS `uploads` (
`id` int(11) unsigned NOT NULL auto_increment,
`name` varchar(200) NOT NULL,
`type` varchar(200) NOT NULL,
`size` int(11) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=24 ;
Default fields are name, type, and size; but you can change that at anytime using the $this->FileUpload->fields = array();
Controller Class:
<?php
function beforeFilter(){
parent::beforeFilter();
//fill with associated array of name, type, size to the corresponding column name
$this->FileUpload->fields = array('name'=> 'file_name', 'type' => 'file_type', 'size' => 'file_size');
}
?>
Depending on whether or not you are using a model your view should hold a file input type with the name of your fileVar.
$this->FileUpload->fileVar = 'file'; //file by default.
Example View WITH Model:
View Template:
<?= $form->create('Upload', array('type'=>'file')); ?>
<?= $form->input('file', array('type'=>'file')); ?>
<?= $form->end('Submit'); ?>
Example View WITHOUT a Model:
View Template:
<form action="controller/action" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" name="Submit" />
</form>
Upon submitting a file the FileUpload Component will automatically search for your uploaded file, verify its of the proper type set by $this->FileUpload->allowedTypes:
Controller Class:
<?php
function beforeFilter(){
parent::beforeFilter();
//defaults to 'image/jpeg','image/gif','image/png','image/pjpeg','image/x-png'
$this->FileUpload->allowedTypes = array('image/jpeg','text/plain');
}
?>
Then it will attempt to copy the file to your uploads directory set by $this->FileUpload->upload_dir:
Controller Class:
<?php
function beforeFilter(){
parent::beforeFilter();
//defaults to 'files', will be webroot/files, make sure webroot/files exists and is chmod 777
$this->FileUpload->uploadDir = 'files';
}
?>
If a fileModel is given, it will attempt to save the record of the uploaded file to the database for later use. Upon success the FileComponent sets $this->FileUpload->success to TRUE; You can use this variable to test in your controller like so:
Controller Class:
<?php
class UploadsController extends AppController {
var $name = 'Uploads';
var $helpers = array('Html', 'Form');
var $components = array('FileUpload');
function admin_add() {
if(!empty($this->data)){
if($this->FileUpload->success){
$this->set('photo', $this->FileUpload->finalFile);
}else{
$this->Session->setFlash($this->FileUpload->showErrors());
}
}
}
}
?>
To View the photo variable you might type something like
View Template:
$html->image("/files/$photo");
At any time you can remove a file by using the $this->FileUpload->removeFile($name); function. An example of that being used might be in a controller:
Controller Class:
<?php
class UploadsController extends AppController {
var $name = 'Uploads';
var $helpers = array('Html', 'Form');
var $components = array('FileUpload');
function admin_delete($id = null) {
$upload = $this->Upload->findById($id);
if($this->FileUpload->removeFile($upload['Upload']['name'])){
if($this->Upload->del($id)){
$this->Session->setFlash('Upload deleted');
$this->redirect(array('action'=>'index'));
}
}
}
}
?>
Simple as that. Automagic File Uploading. I hope you enjoy it. If you read through the documentation I've written in the actual FileUpload Component it will give you detailed examples and explanations of each variable/function. Comments are appreciated.








But when i upload file to sever. file upload to sever with file name with file create bu md5. i can't looking extension of file(example : file.doc, file.pdf).
Can you help me upload file sever with extension *.file extension.
Thanks you!
I use your plugin: it's very helpful...
However I have a problem: In according to tutorial for component setting without model, my controller contains the following beforeFilter
function beforeFilter()
{
parent::beforeFilter();
$this->FileUpload->allowedTypes(array(
'jpg' => array('image/jpeg','image/pjpeg'),
'txt' => array("text/plain"),
'gif',
'sql' => array("application/octet-stream", "text/x-sql"),
'pdf' => array('application/pdf')
));
$this->FileUpload->uploadDir('files/import');
$this->FileUpload->fileModel(null);
}
When I submit the upload request, I get the message: "Unable to save temp file to file system."
I debugged the plugin code and, maybe, I found a bug in /plugins/controllers/components/file_upload.php line 273:
$this->Uploader->file = $this->currentFile;
with this statement the uploader->processFile() method return false, because $this object has this structure
Uploader Object
(
[file] => Array
(
[file] => Array
(
... file infos ...
)
)
If you change the statement in:
$this->Uploader->file = $this->currentFile["file"];
it works perfectly.
In my work case, I try to upload a single file without model and I don't know if my modify is correct for all work cases ( single upload with model, multi upload with/without model ).
Is the modify correct??
I hope my suggestion is helpful :)
Thanks in advance
Also, can i use this to upload a pic in the same form that submits to another model?
I forgot to add the type = file to the edit Form tag.
Like I said, I'm a newb. :)
Works like a charm now if only the hair I pulled out will grow back.
i am using no model for the upload, just uploading and then inserting file names into my model.
This works fine in the Add action, but almost exact same code doesn't work in the Edit action.
I also can't figure out how to post this question on the plugin's site. :(
Is there a way to ensure that it uploads once I post? It seems to do it automagically on the Add form, but I can get no errors or success for FileUpload from the Edit form.
I am having one problem though, I want to use it to upload zip files but i keep getting an error
FileUpload::_checkType() application/octet-stream is not in the allowedTypes array. [APP/controllers/components/file_upload.php, line 269]
I have put every type of compressed file I can think of in the allowed types but I still can't get it to work
var $allowedTypes = array(
'application/zip',
'application/x-compress',
'application/x-compressed',
'application/x-gzip',
'application/x-gtar',
'application/x-tar'
);
and in my beforeFilter function I have
parent::beforeFilter();
$this->FileUpload->fileModel = null; //Upload by default.
$this->FileUpload->allowedTypes = array('application/zip');
$this->FileUpload->uploadDir = 'files/'.$name;
If I set this to upload PDFs or text files it works perfectly. Any ideas what I am doing wrong?
Thanks
I have updated the article accordingly.
Here is a direct link: http://github.com/webtechnick/CakePHP-FileUpload-Plugin
Can you provide read only credentials so we can checkout the latest source? Even better you could set up a repository on github so we can help keep it up to date with the core.
View Template:
<?= $form->create('Upload', array('type'=>'file')); ?>
<?= $form->input('file', array('type'=>'file')); ?>
<?=$form->input('is_published',array('type'=>'hidden','value'=>1));?>
<?=$form->input('storyboard_id', array('type'=>'hidden', 'value'=>$storyboard['Storyboard']['id']));?>
<?= $form->end('Submit'); ?>
(and I also have an is_published field there just for kicks and giggles).
Then, I have a storyboard_id in my table Uploads (among with other things):
CREATE TABLE `moody3D`.`uploads` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(200) NOT NULL,
`type` varchar(200) NOT NULL,
`size` int(11) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
`is_published` tinyint(4) DEFAULT '1',
`screenplay_id` int(12) DEFAULT NULL,
`storyboard_id` int(12) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=11 DEFAULT CHARSET=latin1;
And finally, I add this to FileUploadComponent on line 220:
Component Class:
<?phpforeach($this->data['Upload'] as $field_name=>$field_value) {
if ($field_name!="file") {
$save_data[$field_name] = $field_value;
}
}
?>
This basically puts all form fields into $save_data variable, which then gets passed to save() method of the Uploads model, and voila! All my data is saved.
An example on how to expand this further: to add a description to an upload, I'd create another column to the table:
alter uploads add column `descr` text null;
and add another field to the form:
<?=$form->input('descr');?>
That should do it. I think my modification should go with this component by default.
PS. Thanks for the component, I find it useful. Better than to write my own thing.
I added the following:
/***************************************************
* foreignKeys is an array of the foreign keys in the database
* @var array
* @access public
*/
var $foreignKeys = array();
In the function _processFile(), right after where it says
$save_data[$this->fields['size']] = $this->uploadedFile['size'];
add the following:
foreach($this->foreignKeys as $foreignKey) {
$save_data[$foreignKey] = $this->uploadedFile[$foreignKey];
}
And in the function _uploadedFileArray(), right after where it says
if($this->uploadDetected && $retval === false){
$this->_error("FileUpload: A file was detected, but was unable
to be processed due to a misconfiguration of FileUpload.
Current config -- fileModel:'{$this->fileModel}'
fileVar:'{$this->fileVar}'");
}
add the following:
if(!empty($this->foreignKeys)) {
foreach($this->foreignKeys as $foreignKey) {
if(isset($this->data[$this->fileModel][$foreignKey])) {
$foreignKey = array($foreignKey =>
$this->data[$this->fileModel][$foreignKey]);
$retval = array_merge($retval, $foreignKey);
}
}
}
I uploaded the modified code to
http://www.mediafire.com/file/rk2qodkmwih/file_upload.php I hope this helps.
class Promo extends AppModel {
var $name = 'Promo';
var $belongsTo = 'Upload';
}
Will $Promo->save( $this->data ); save the Upload also? Assuming that my form contains data[Upload][file] of course.
Thanks
text/csv csv
text/comma-separated-values csv
to my mime types.
But when ever I upload a CSV file it cant detect that it is a CSV type.
If I do debug($this->params['form']); in the _uploadedFileArray() function,
It returns the filename, size and temp name. But type is empty.
Any other file type works fine!
Apparently its a Google Chrome problem, http://code.google.com/p/chromium/issues/detail?id=5633#c1 Anyone else have experience with this?
Just did a comparison with other browsers of what they detect a file with csv extension as:
ie -application/octet-stream
opera -text/comma
firefox -application/octet-stream
chrome
Can anyone shed any light on this?
eng: that bug in v3.6 related to non-model users fucked up my brains on friday and today in the morning. Lucky me, that I've updated plugin from SVN and there was bug fix. Anyway, seems to be working now, thanx :)
Read more here:
http://www.webtechnick.com/blogs/view/224/FileUpload_Plugin_v3_5_Multiple_File_Uploads
NEW SVN:
http://svn2.xp-dev.com/svn/nurvzy-file-upload-pluginIf you've currently checked out this plugin you can use svn switch to change the repo so you can get the latest updates.
svn switch --relocate https://svn2.xp-dev.com/svn/nurvzy-file-upload-plugin http://svn2.xp-dev.com/svn/nurvzy-file-upload-pluginIf you have any problems, let me know.
if (!is_dir($up_dir)):
mkdir($up_dir);
endif;
I have an activities form, so my form helper is create('Activity') and I noticed it should be 'Upload' but then my Activity data wont get saved.
What is the best way to tackle this?
If I use this in Activity
function beforeFilter() {
$this->FileUpload->fileModel = 'Activity';
}
The Activity is saved and the file is uploaded but the file data is not saved in the table. Should I just save it like Activity? Or is there a setting for the upload controller?
I'd also like to save the activity_id in the upload table but I should be able to figure that out ;)
Thanks
Jason
svn checkout https://svn2.xp-dev.com/svn/nurvzy-file-upload-plugin file_uploadExample:
View Template:
<?php $image_path = $fileUpload->image('image.jpg', array('width' => 100, 'image_path_only' => true));
// $image_path = '/files/resized/imagex100.jpg'
// (default settings, directory may change depending on your settings.)
?>
Read more @ http://www.webtechnick.com/blogs/view/221/CakePHP_File_Upload_Plugin
1) You can make use of the uploadDetected and finalFile features of the FileUpload component in your controller.
Controller Class:
<?phpif($this->FileUpload->uploadDetected && !$this->FileUpload->success){
//Someone tried to upload something but it failed due to validation.
//remove the finalFile so you don't keep orphaned copies around.
$this->FileUpload->removeFile($this->FileUpload->finalFile);
}
?>
2) Use javascript to check your validations before your user actually submits the content. There are a few great javascript validation helpers here in the bakery you can use if you like that route -- that way your user wouldn't have to upload the file twice.
I hope that helps,
Nick
Thanks for this. I have added it to a project I am building and it has solved a lot of headaches for me.
I need to ask a fairly dumb question - one that perhaps I should know the answer to if I weren't so new to Cake.
I am using additional fields and they are saving as expected. However, if validation fails on one or more of the extra fields Cake presents the validation errors as expected but the file has already been uploaded. The user will of course correct the error and go again. But now I have uploaded the file twice and the first is effectively orphaned. Over time that will become a substantive problem. I guess my dumb question is "how can I perform validation before doing the upload?".
Thank you.
Indeed MeioUpload is an excellent behavior, and I encourage everyone to take a look at it if you're in the market for an upload utility. However, the reason I created FileUploadComponent in the beginning was because I wanted a solution that didn't require a model or a table. I originally wanted a component that I could use to upload files to my server without firing up a terminal or my favorite ftp program (for administrative purposes) -- I had no need for a model or a table, I just wanted an easy way to upload files. It's grown since then, for the better I like to think, but the model is still optional unlike a behavior.
Thanks again for catching that typo.
Hope all is well,
Nick
var $uploadeDetected = false;I assume it should read as follows:
var $uploadDetected = false;This does probably not affect the logic though, since an uninitiated variable gives about the same result (?) as a boolean set to false in PHP. However, it may provoke unexpected behaviour in future versions.
Great work, writing this component! Nevertheless, I would like to hint about http://www.meiocodigo.com/projects/meioupload/, which is an upload behaviour. The reason I want to do this is because it might be more appropriate to put this kind of logic into a behaviour than a component. I changed to the MeioUpload's follow-up behaviour in a small project, which felt a lot better.
Try doing a
debug($this->uploadedFile);
debug($save_data);
exit();
right before $model->save(); so you can see what's being passed to $model->save() and make sure its actually getting the data you think it should.
Which version of FileUploadComponent are you using? You can download the latest component at http://projects.webtechnick.com/file_upload.tgz.gz
Also, I respond to issues a bit quicker via email: nick@webtechnick.com . If you still need help that's usually the best way to get a quick response.
Hope that helps,
Nick
I used a different model name, so I set this in the beforeFilter().
The files are being uploaded correctly, however the data about the file (name, type, size) is not being stored in my database.
Do you have an idea of what the problem could be?
Thanks
Hi Sarah,
Thanks for using the component, unfortunately without more to go on I can only speculate and offer steps to help debug the problem. Make sure the model name is exactly the name of your model (case sensitive and camelcase), because the way the component gets the model is via ClassRegistry::init(). Is $this->FileUpload->success being set to true after an upload? If not, and a model is set, the only reason is because $model->save() failed; make sure the component isn't hitting model validation errors.
You can also check the components errors and logs to see if it ran into any permissions issues or the like (although you say its being uploaded correctly so this is a long shot).
I suggest using a tool like http://thechaw.com/debug_kit to view the sql being executed when you upload a file. And if all else fails, try putting in some echo statements around $model->save.
I hope that helps,
Nick
Thanks for responding. :)
$this->FileUpload->success is being set to true. The weird thing is that $model->save() was also successful.
I'm not sure how to check the component error logs...
From the sql calls at the bottom of the page I can see that the created, modified, and approval fields are being inserted. There are no calls about the name, type, or size. Is there a way I can check this data? (sorry, I'm kinda new to cakephp)
Another weird thing (and this may be totally irrelevant), but the entries are duplicated in the table...(I only get one copy of the file uploaded though).
I appreciate your help.
Thanks,
~S
I need to be able to save a photo (gif) and brochure (pdf) along with the rest of the form information.
cakePHP is a great framework, but sometimes it's amazing to me that the simplest of things have yet to have been incorporated into it.
svn co https://svn.xp-dev.com/svn/nurvzy_file_upload/I baked all the stuff from the table created like descripted...
But then I added these two lines to the
removeFile Function of the fileupload component.
$up_dir = $this->uploadDir;
$target_path = $up_dir . DS . $name;
The rest worked well ;)
Mattes
Undefined variable: up_dir
If I fix that problem, then I get a similar error for the $target_path variable.
I don't see how these two variables are getting defined in this method. Is this an error, or am I missing something?
component and helper works fine, but i have some questions/thoughts:
- delete file doesn't delete al cached files, why?
- and is it possible to use also the height for generating files?
- is it possible to let the helper generate only the url for the image? for thickbox?
Thanks again for the component!
if(!$model || $model->save($save_data)){
$this->success = true;
$this->uploadId = $model->id;
}
line 271 uses the $model->id... but I'm not using model, so it's null. I just changed mine to this:
$this->uploadId = !$model ? $this->uploadId : $model->id;
because I'm not sure how/where $this->uploadId is being used.
Thanks again for the component!
Certain images (I've only tested with .jpg) break the helper so that every image displayed on the same page are shown in original size.
Example:
I upload 4 images and display their thumbnails to the same page. I then upload a fifth image, and suddenly they're all displayed in the original size.
Edit: the images displayed AFTER the broken/breaking image are displayed in original size.
I've uploaded the same image twice, and one of them worked while the other didn't.
fileUpload->image();
- Now defaults to not resize the image.
- New 'resizedDir' option available for the user to organize their resized images into a different directory than the originals. default behaviour is put in the same directory but I suggest changing it to 'resized'. (webroot/files/resized must be chmod 777).
- New resizeThumbOnly variable that defaults to true. Meaning the helper will only resize images with widths requested smaller than the original.
Download: http://projects.webtechnick.com/file_upload/views/helpers/file_upload.php SVN:
svn co https://svn.xp-dev.com/svn/nurvzy_file_uploadComponent Class:
<?php
function _uploadedFileArray(){
if($this->fileModel){
$retval = isset($this->data[$this->fileModel][$this->fileVar]) ? $this->data[$this->fileModel][$this->fileVar] : false;
} else {
$retval = isset($this->params['form'][$this->fileVar]) ? $this->params['form'][$this->fileVar] : false;
}
// check for multiple file input from multi-style php [] input name using something like jQuery.MultiFile
if ($retval !== false && !isset($retval[0])) {
// save single file info as if it were first multiple file array item
$single_file_array = $retval;
// clear return value
$retval = array();
// set single item in new multi file array
$retval[0] = $single_file_array;
}
if($this->uploadDetected && $retval === false){
$this->_error("FileUpload: A file was detected, but was unable to be processed due to a misconfiguration of FileUpload. Current config -- fileModel:'{$this->fileModel}' fileVar:'{$this->fileVar}'");
}
return $retval;
}
?>
Now _processFile() needs to do what it does except iterate through the items in $this->uploadedFile. This will work whether or not you are uploading multiple files.
Another improvement would be storing the IDs for files uploaded as well as creating a roll back method.
The way PHP (and all other languages I know of) in an ||(OR) operator is execute through the condition until true is returned and immediately exit with true. The same is also the case for &&(AND) returning false; it will execute through the condition until false is returned and immediately exit with false.
Examples:
function returnsFalse(){ return false; }
if(true || returnsFalse()){
//code here will run. returnsFalse() is never evaluated.
}
if(returnsFalse() || true){
//code here will still run but returnsFalse() was evaluated.
//this is slower than the previous condition for that reason.
}
So $this->DoclibDocument->save($this->data) is only executed if $this->FileUpload->success is false.
You can leverage this to your benefit improving computation time in all your applications by running the most likely circumstance first short-cutting all other evaluations.
PHP Logical Operators: http://us3.php.net/manual/en/language.operators.logical.php
Hope that helps,
Nick
I know we're digressing into the realm of PHP here, but would that process the first condition $this->FileUpload->success and only process the second $this->DoclibDocument->save($this->data) if the first was false.
Or would it run both and return true if either were true? Which would result in $model->save being ran twice.
Try this in your edit:
if ($this->FileUpload->success || $this->DoclibDocument->save($this->data)){
//do success stuff
} else {
//do failure stuff
}
That will only do one or the other.
My implementation uses a model with extra fields (author and summary). I am relying on the component's ability to call $model->save and handle validation when adding and editing documents.
This works as long as you always include a file when submitting your forms which is fine when adding documents, but not editing them and only wanting to update the Author for example.
If no file is supplied when the form is submitted the process fails without any errors ($this->FileUpload->showErrors() returns nothing).
However, when a file is supplied the process moves on and $model->save is called which properly validates the model data as desired, including errors being passed back to the form via the $this->validationErrors array.
Am I looking at this the wrong way and should I be checking my data in the controller and only calling FileUpload if a file has been submitted?
If so, why do we save extra model data upon adding a file, but not on editing one?
Please understand, I'm not trying to pick holes, just trying to understand the correct way to implement this component.
For reference here are my add and edit methods:
function add($doclib_category_id) {
if (!empty($this->data)) {
$this->data['DoclibDocument']['creator_id'] = $this->authUser['User']['id'];
$this->data['DoclibDocument']['modifier_id'] = $this->authUser['User']['id'];
if($this->FileUpload->success){
$this->Session->setFlash('Document added');
$this->redirect('/DoclibCategories/view/'.$doclib_category_id);
} else {
$this->set('fileErrors', $this->FileUpload->showErrors());
$this->Session->setFlash('Document not added, correct errors and resubmit');
}
} else {
$this->data['DoclibDocument']['doclib_category_id'] = $doclib_category_id;
}
}
function edit($doclib_category_id, $id = null) {
if (!empty($this->data)) {
$this->data['DoclibDocument']['modifier_id'] = $this->authUser['User']['id'];
if ($this->FileUpload->success) {
$this->Session->setFlash('Document updated');
$this->redirect('/DoclibCategories/view/'.$doclib_category_id);
} else {
$this->set('fileErrors', $this->FileUpload->showErrors());
$this->Session->setFlash('Document not updated, correct errors and resubmit');
}
}
$this->data = $this->DoclibDocument->find('first', array(
'conditions' => array('DoclibDocument.id'=>$id),
'contain' => array()
)
);
}
I needed to upload file to a non web accessible folder.
i changed
$up_dir = WWW_ROOT . $this->uploadDir;
to
$up_dir = $this->uploadDir;
this allowed me to set the upload directory in the controllers before filter for any folder i wished.
i wanted to have file names as uuids this ensured they were unique and a bit cleaner that your implementation.
in process function change
$target_path = $up_dir . DS . $this->uploadedFile['name'];
$temp_path = substr($target_path, 0, strlen($target_path) - strlen($this->_ext())); //temp path without the ext
//make sure the file doesn't already exist, if it does, add an itteration to it
$i=1;
while(file_exists($target_path)){
$target_path = $temp_path . "-" . $i . $this->_ext();
$i++;
}
to
$target_path = $up_dir . DS . String::Uuid() . $this->_ext();
another tweak of process file was to set $finalfile to hold the value of $save_data this allowed me to have the information i needed to save the file in my current save routine.
I was using "application/pdf", "application/x-pdf" as the allowable types. And I'm using FileUpload without a model.
I created an application last week, specifically for uploading PDFs (using the FileUpload component plus a model).
The only issue I had, was in the lack of error verbosity at the time, as it turned out some PDFs would upload, others wouldn't, with no error. I found that some PDFs were exceeding the max upload size in php.ini, so I increased the allowable size, and made modifications to FileUpload to be more verbose regarding such errors.
Make sure you're using the latest copy of FileUpload from Nick's download page, as it has the more verbose error handling included.
That was exactly what it was. I spent hours trying to figure that one out and it never occurred to me about the file size issue. Thanks Jon for the answer. That really helped.
@Nick, btw great work on the FileUpload component! Thanks.
Hey, I think I understood the bug with PDF files for the version 5.* . some pdfs MIME types seems to have double quotes, like this : "application/pdf". In my allowedTypes array, I did'nt used quotes.
To fix the bug, I just deleted double quotes before processing files.
In components/file_upload.php, add the next line in the method _uploadedFilesArray(), after ' foreach($retval as $key => $file){' :
$retval[$key]['file']['type'] = str_replace('"', '', $file['file']['type']);
@Nick : it's a very nice and useful component ! Thanks a lot !
(Sorry for my bad english => I'm french ;))
Notice (8): Undefined index: [APP\controllers\components\file_upload.php, line 257]
Notice (8): Trying to get property of non-object [APP\controllers\components\file_upload.php, line 268]
Warning (2): Cannot modify header information - headers already sent by (output started at C:\Servers\Apache2\htdocs\jivox\cake\basics.php:313) [CORE\cake\libs\controller\controller.php, line 615]
From what I can tell, if fileModel = null, I get an undefined index error. To overcome this, I do a test before hand
isset($this->fileModel)
Not sure if this is a right approach.
Thanks for catching that, I've made the appropriate fix for non-model users in the latest release of FileUploadComponent (v2.0.1).
Here's the fix:
//Ability to dynamically add other model fields added by Jon Langevin$save_data = array();
if($this->fileModel){
$save_data = $this->data[$this->fileModel];
unset($save_data['file']);
}
Or Download the full 2.0.1 component from here: http://projects.webtechnick.com/file_upload
SVN:
svn co https://svn.xp-dev.com/svn/nurvzy_file_uploadDOWNLOAD:http://projects.webtechnick.com/file_upload
The FileUploadHelper give you great deal of HTML automation to your FileUploadComponent.
1) Automatic image resizing (thumbnails) for upload images (requires the GD Library). If there is no GD library present the helper uses browser resizing.
2) Input field configured based on your component configuration.
Image examples: Resizes, saves, and displays 'image.jpg' to width 250 (if GD library is installed).
$fileUpload->image('image.jpg', array('width' => 250));If you have a custom uploadDir for each file you can set it in the options.
$fileUpload->image('image.jpg', array('width' => 250, 'uploadDir' => 'files'));You can pass any other options as you would an html->image call
$fileUpload->image('image.jpg', array('width' => 250, 'alt' => 'image', 'id' => 'the_image'));File Input Examples fileUpload->input() defaults to your default FileUploadComponent configuration. (ie. fileModel => 'Upload', fileVar => 'file'). Changing your default FileUploadComponent configuration will also change the default FileUploadHelper input. YAY!
$fileUpload->input(); //same as $form->input('Upload.file', array('type' => 'file'));You can pass custom fields into the input.
$fileUpload->input(array('var' => 'fileVar', 'model' => 'Picture')); //same as $form->input('Picture.fileVar', array('type' => 'file'));You can pass any other options as you would into $form->input
$fileUpload->input(array('label' => 'Picture Upload', 'div' => false)); //same as $form->input('Upload.file', array('type' => 'file', 'label' => 'Picture Upload', 'div' => false));I hope you find the auto image thumbnails and input useful. I will be adding a new article soon.
1) Download/SVN Update to the newest FileUploadComponent (I've fixed it)
2) Validate with javascript prior to submit - so you won't hit any validation errors on saving.
3) clear out $this->data['fileModel']['fileVar'] if you hit a validation error in the controller.
if($this->User->save($this->data)){
//yay!
}
else{
//oh nos!!
unset($this->data['Upload']['file']);
}
I suggest solution 1. *smile*
Warning (512): No file was uploaded. [APP\controllers\components\file_upload.php, line 316]Code | Context
$text = "No file was uploaded."
$message = "No file was uploaded."
$message = __($text,true);
$this->errors[] = $message;
trigger_error($message,E_USER_WARNING);
FileUploadComponent::_error() - APP\controllers\components\file_upload.php, line 316
FileUploadComponent::_checkFile() - APP\controllers\components\file_upload.php, line 347
FileUploadComponent::startup() - APP\controllers\components\file_upload.php, line 193
Component::startup() - CORE\cake\libs\controller\component.php, line 112
Dispatcher::_invoke() - CORE\cake\dispatcher.php, line 210
Dispatcher::dispatch() - CORE\cake\dispatcher.php, line 194
[main] - APP\webroot\index.php, line 88
Warning (512): No file was uploaded. [APP\controllers\components\file_upload.php, line 318]Code | Context
$text = "No file was uploaded."
$message = "No file was uploaded."
$message = __($text,true);
$this->errors[] = $message;
trigger_error($message,E_USER_WARNING);
FileUploadComponent::_error() - APP\controllers\components\file_upload.php, line 318
FileUploadComponent::_checkFile() - APP\controllers\components\file_upload.php, line 349
FileUploadComponent::startup() - APP\controllers\components\file_upload.php, line 194
Component::startup() - CORE\cake\libs\controller\component.php, line 112
Dispatcher::_invoke() - CORE\cake\dispatcher.php, line 210
Dispatcher::dispatch() - CORE\cake\dispatcher.php, line 194
[main] - APP\webroot\index.php, line 88
Unfortunately, you're running into a browser security issue. Browsers cannot access the file system without direct user interaction. Cakephp cannot set the default of a file field after a redirect/render, nor can javascript, or even HTML, it's just not allowed.
There is, however, a workaround that I use regularly. Use javascript to validate your user's input prior to submitting the form. That way you'll prompt your user with errors without actually hitting the server. This guarantees that by the time you're hitting $this->model->save() you will have valid user input.
There's a really great helper that can do a lot of the heavy lifting for you.
Checkout: http://bakery.cakephp.org/articles/view/automagic-javascript-validation-helper
Example: http://sandbox2.pseudocoder.com/demo/validation
Hope that helps,
Nick
Any advice on how to deal with that?
This is what I am using:
<h1><?=$this->pageTitle;?></h1>
<?= $form->create("FeedbackSubmission", array("action"=>"edit", "type" => "file")); ?>
<?= $form->input("FeedbackSubmission.id"); ?>
<?= $form->input("FeedbackSubmission.feedback_type_id"); ?>
<?= $form->input("FeedbackSubmission.title"); ?>
<?= $form->input("FeedbackSubmission.description"); ?>
<?= $html->image( ("/files/" . $this->data["Upload"]["name"]), array( "width" => "250") ); ?>
<?= $form->input("FeedbackSubmission.user_id", array("type"=>"hidden","value"=>"1")); ?>
<?= $form->input('Upload.file', array('type'=>'file')); ?>
<?= $form->button("Save", array( "type" => "submit" )); ?>
<?= $form->end(); ?>
Replace the current _checkFile() function with:
/***************************************************
* Checks if there is a file uploaded
*
* @return void
* @access protected
*/
function _checkFile(){
if($this->uploadedFile){
if($this->uploadedFile['error'] == UPLOAD_ERR_OK ) {
return true;
}
else {
$this->_error($this->upload_errors[$this->uploadedFile['error']]);
}
}
return false;
}
This new code is in the repository, so you can just do an "svn update" or download the new code.
In regards to the helper; it's still under development. The goal is to use it in conjuction like the Pagination features but it has a long way. Right now you have to configure the helper the same way you configured the component for it to work. If you're feeling gutsy you're welcome to play around with it, contribute, submit bugs etc...
I'll post again with documentation when I'm happy with the helper.
Might I suggest you add an option to hash or timestamp the uploaded filename as to avoid duplicate filenames?
svn co http://svn.xp-dev.com/svn/nurvzy_file_uploadWhat errors are you receiving? Did you make sure you added FileUpload to the components array in your other controller?
Another suggestion is regardless of what controller or form you're using you'll want to use your model.fileVar syntax in your form input helper.
Example:
$form->create('User', array('type' => 'file'));
$form->input('User.field');
$form->input('Upload.file', array('type' => 'file'));
$form->end('Submit');
In your controller you might do something like...
function add(){
if(!empty($this->data)){
if($this->FileUpload->success){
$this->data['User']['upload_id'] = $this->FileUpload->uploadId;
}
if($this->User->save($this->data)){
...
}
}
}
The uploadId trick is a new feature in the component you'll need to either download the latest FileUploadComponent (http://projects.webtechnick.com/file_upload) or svn checkout the latest version.
I hope that helps,
-Nick
$this->data['User']['upload_id'] = $this->FileUpload->uploadId;You might want to edit your post to fix that.
After this it started to work great. But now that I have it working, your controller component is throwing an error:
Notice (8): Undefined index: [APP\controllers\components\file_upload.php, line 347]Warning (512): [APP\controllers\components\file_upload.php, line 318]
Greetings from Argentina,
blue
svn checkout https://svn.xp-dev.com/svn/nurvzy_file_uploadAdd the following array to the top of the FileUpload class:
/**
* Definitions of errors that could occur during upload
*
* @var array
*/
var $upload_errors = array(
UPLOAD_ERR_OK => 'There is no error, the file uploaded with success.',
UPLOAD_ERR_INI_SIZE => 'The uploaded file exceeds the upload_max_filesize directive in php.ini.',
UPLOAD_ERR_FORM_SIZE => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.',
UPLOAD_ERR_PARTIAL => 'The uploaded file was only partially uploaded.',
UPLOAD_ERR_NO_FILE => 'No file was uploaded.',
UPLOAD_ERR_NO_TMP_DIR => 'Missing a temporary folder.', # Introduced in PHP 4.3.10 and PHP 5.0.3.
UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk.', # Introduced in PHP 5.1.0.
UPLOAD_ERR_EXTENSION => 'File upload stopped by extension.' # Introduced in PHP 5.2.0.
);
Then replace the function _checkFile() with this code:
/***************************************************
* Checks if there is a file uploaded
*
* @return void
* @access protected
*/
function _checkFile(){
if($this->uploadedFile && $this->uploadedFile['error'] == UPLOAD_ERR_OK ) {
return true;
} else {
$this->_error($this->upload_errors[$this->uploadedFile['error']]);
return false;
}
}
I use this component in DocumentsController, which has the model Document.
In DocumentsController::beforeFilter(), set the model to 'Document'.
The Document model must be prepared to accept the 'name', 'type', 'size' variables that FileUpload already saves to the database.
In your view where you submit a file (in the Document view files), you can now add additional inputs that you want to have saved via your Document controller.
Only 2 lines were changed (1 change 1 add):
$save_data = $this->data[$this->fileModel];
unset($save_data['file']);
The full FileUpload::processFile() method is below.
function processFile(){
$up_dir = WWW_ROOT . $this->uploadDir;
$target_path = $up_dir . DS . $this->uploadedFile['name'];
$temp_path = substr($target_path, 0, strlen($target_path) - strlen($this->_ext())); //temp path without the ext
//make sure the file doesn't already exist, if it does, add an itteration to it
$i=1;
while(file_exists($target_path)){
$target_path = $temp_path . "-" . $i . $this->_ext();
$i++;
}
$save_data = $this->data[$this->fileModel];
unset($save_data['file']);
if(move_uploaded_file($this->uploadedFile['tmp_name'], $target_path)){
//Final File Name
$this->finalFile = basename($target_path);
$model =& $this->getModel();
$save_data[$this->fields['name']] = $this->finalFile;
$save_data[$this->fields['type']] = $this->uploadedFile['type'];
$save_data[$this->fields['size']] = $this->uploadedFile['size'];
if(!$model || $model->save($save_data)){
$this->success = true;
}
else{
$this->success = false;
}
}
else{
$this->_error('FileUpload::processFile() - Unable to save temp file to file system.');
}
}
http://projects.webtechnick.com/file_upload/
If you click on a .php file, the server tries to execute that file. iirc, you should be able to place a .htaccess file that overrides the handler for .php to display as if it were .phps, just a thought.
Is there any way of using/modifying this component to upload multiple files? I've tried but failed :( ...
Just started with php programming/cakephp, the upload works perfect. But i cant(don't know how to) show the pictures
Thnx already
-edit- I'm not using a database btw, just uploading to the files folder
I recommend using the HtmlHelper method image.
DOC: http://book.cakephp.org/view/206/Inserting-Well-Formatted-elements API: http://api.cakephp.org/class/html-helper#method-HtmlHelperimage
You can use it in any view you've included 'Html' as one of the available helpers (in every controller by default).
Then you would code:
<?php echo $html->image('/files/image_name.jpg'); ?>
Or you can just flat out write the HTML:
<img src="/files/image_name.jpg" alt="Image" />
Don't forget that leading '/' in the source. Everything is relative to your WWW_ROOT directory which is normally 'app/webroot/'. If you've left everything as default all your uploaded files will be in 'app/webroot/files'. If you've changed the uploadDir directory you'll have to make the appropriate changes to the image source.
Hope that helps.
Cheers and thnx
So, in my controller I set the fileName and call processFile() method, just like Elmer did on his comment.
$data_clipping = explode('/',$this->data['News']['data']);
if ($this->FileUpload->hasFile) {
$this->FileUpload->fileName = 'clipping_'.$data_clipping[0].$data_clipping[1].$data_clipping[2].'.pdf';
$this->FileUpload->processFile();
}
I've posted the complete component on CakeBin: http://bin.cakephp.org/view/608727117
Thanks for this great component!
walter
But I want to inform about one problem: web interface of component download does not work - when I try to download file I get error "Fatal error: Class 'Object' not found in /home/webtechn/projects/file_upload/controllers/components/file_upload.php on line 13"
Also I would like to ask you to post example of some controller with all operations (index/add/view/delete). I have managed to add images by reading this post, but I think if there was some example it would be more valuable than thousand words in description.
First of all, this is a fine piece of work. I'm using it with an existing model, so I encountered some issues using 1.5 (r11 from svn) that I circumvented. Maybe you will find it useful?
My field name is entry_upload, so when I print_r this->data, the field looks like:
[entry_upload] => Array
(
[name] => application_cascade.png
[type] => image/png
[tmp_name] => /tmp/phpllSu9X
[error] => 0
[size] => 524
)
It's an array, so when I save, there is an error. So I set "entry_upload" to "name":
$this->data[$this->FileUpload->fileModel][$this->FileUpload->fileVar] = $this->data[$this->FileUpload->fileModel][$this->FileUpload->fileVar]["name"];
And then in processUpload(), success is set to true if the file was moved correctly.
It's quite hackish as I didn't have much time to work on it, but it works. Of course, the model (not FileUpload) is now responsible for saving the data.
That's fine since I only want to have one image per row.
Though I was wondering, what would be the best way to update files. I want to create an edit action on my UploadsController to do this, giving the users the possibility to upload a new file to the same Upload record in the database. The old file should be deleted and a new file should be linked to the database record.
Any ideas? Please let me know...
Sytze
svn checkout https://svn.xp-dev.com/svn/nurvzy_file_uploadthanks for the component...
It didn't work out for me, it threw an error: FileUpload: A file was detected, but was unable to be processed due to a misconfiguration of FileUpload. Current config -- fileModel:'Upload' fileVar:'file' although no file data was submitted. I tracked it down to function _multiArrayKeyExists() which returned TRUE, the problem was I had multiple records in one form, so the array looked like this:
and comparing 'tmp_name' == 0 returned true...(
[Model] => Array
(
[0] => Array
(
[field1] => xxx
[field2] => yyy
)
)
)
Adding strict comparison did the trick:
Line 363:
I found no bugtacker for this component, so I'm submitting it as a comment. Hope it helps someone.if ($needle===$key) {
Greetings,
fool
@Elmer: Thanks for contributing! That's a great addition to the component. I hope you don't mind I included it into the version 1.5 release. I've given you credit in the readme as well as the actual component.
SVN:
svn checkout https://svn.xp-dev.com/svn/nurvzy_file_uploadDOWNLOAD: http://projects.webtechnick.com/file_upload/
Let me know if you have any issues with either, this is the first project I've made public so I hope I got it right. =)
I ended up putting application/unknown in the list of allowed types to get it to work. I'm not too worried about bad uploads because it's a small user-base.
I was using Firefox on a Mac and did not test in any other browsers.
This section:
= $form->create('Upload', array('type'=>'file'); ?>
= $form->input('file', array('type'=>'file'); ?>
= $form->end('Submit'); ?>
is missing some parenthesis at the ends of the first two lines and should be like this:
= $form->create('Upload', array('type'=>'file')); ?>
= $form->input('file', array('type'=>'file')); ?>
= $form->end('Submit'); ?>
I've added 2 vars:
/***************************************************
* holds true if an upload is pending and needs to be processed
*
* @var boolean
* @access public
*/
var $hasFile = false;
/***************************************************
* if true: files are processed as soon as they come in
* if false: when a file is ready hasFile is set to true
* it is then up to the calling application to call processFile()
* whenever it wants. this allows params to be changed per uploaded file
* (save every file in a different folder for instance)
*
* @var boolean
* @access public
*/
var $automatic = true;
I've changed the startup() routine, here's the full code (the call to processFile() has changed):
function startup(&$controller){
$this->uploadDetected = ($this->_multiArrayKeyExists("tmp_name", $this->data) || $this->_multiArrayKeyExists("tmp_name",$this->data));
$this->uploadedFile = $this->_uploadedFileArray();
if($this->_checkFile() && $this->_checkType()){
$this->hasFile = true;
if ($this->automatic) { $this->processFile(); }
}
}
And I've made the processFile method public by removing the underscore from its name.
What it does
As long as the automatic var is true (default), the component works as always. But if it is set to false, processFile() is no longer called automatically. From my controller I can now set an upload folder and call processFile() when I'm ready.
if ($this->FileUpload->hasFile) {
$this->FileUpload->uploadDir = 'files/sub/dir/1/2/3';
$this->FileUpload->processFile();
}
I'm fairly new to cakephp, and I'd like to mention one thing that threw me off. You have to put 'type' => 'file' in the form create. It was just something I missed at first glance and wasted a bit of time on. If you do this there is no visible error message.
Thanks very much for making this, it saved me a lot of time.
Thank you for your kind words. There isn't a native way to add fields like "user_id" into the save other than how you did it directly in the _processFile method.
I wrote it thinking people would use it (as I do) as a foriegn key into other tables like:
CREATE TABLE IF NOT EXISTS `pictures` (
`id` int(11) unsigned NOT NULL auto_increment,
`title` varchar(200) NOT NULL,
`description` text NOT NULL,
`category` varchar(200) NOT NULL,
`upload_id` int(11) unsigned NOT NULL,
`user_id` int(11) NOT NULL,
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
Pictures is what I use as a gallery type UI. But you're right, it sure would be nice to be able to save other data (such as "user_id" and the like) with the uploaded data directly in the specified model. I'll make that a priority in the next release.
Thank you for your comments and suggestions. I'm glad you found it useful!
Thank you for pointing that out. Added the } so no one else will have to go through your pain.
$i = 1;
while(file_exists($target_path)){
$target_path = $temp_path . "-" . $i . $this->_ext();
$i++;
}
Let say that I already heave files with names "file1" up to "file99999999"
This will extract file extension ONLY 99999999 + 1 times in this particular case.
Besides, looks like "copy/paste" ( where are nice camel-cased variables / methods names ? ).
Indeed, I've yet to try and upload one hundred million files named the same, I'm not even sure its OK to have one hundred million files in a single directory. I really should create a smarter distribution of uploaded files, I'll work on that for the next version.
What did the error messages say? Did you use with or without a model? I'm curious why it didn't work, would like to know how I can make it more user friendly.
Not 100% what you mean by "copy/paste". I copy/pasted from my component file and use copy/paste to use and re-use the beforeFilter() examples. I like things spelled out for me in articles when I'm trying something new, I was hoping to provide the same benefit.
In regards to camel-case variables/methods you're right; that's a hard habbit for me to kick in cakePHP, coming from RoR only classes are camel-case everything else is underscore. Fixed.
Comments are closed for articles over a year old