Recent Thoughts

Downloads

Contact Us

Email:
Topic:
Message:

Google Ad

File Upload Plugin v4.0 -- Behaviors and Global Settings

Date: Fri, Nov 20th 2009, 20:37 Author: nick Views: 2550 Comments: 56 share

CakePHP File Upload Plugin

Info:
Get it:
  • Download Now
  • svn co http://svn.github.com/webtechnick/CakePHP-FileUpload-Plugin file_upload
New in version 4.0 of the File Upload Plugin is a new behavior and global settings classes. As most of you who are familiar with File Upload Plugin know, it was rather odd that when you setup the FileUpload component the way you wanted in your controller you then had to set it up again in your view for the helper to send the component the right information. Well, all that is gone with a new global settings class. For those of you not familiar with the File Upload Plugin, if you are developing a site that allows a display picture or avatar, perhaps one similar to http://www.partypoker.com/ you will know dealing with uploads can be tricky. Hopefully this plugin will clear up any issues you may have.

Settings you save in plugins/file_upload/config/file_upload_settings.php will be the new "default" settings across all your file uploading instances. This is true for the component, the helper, and the new behavior. By opening up this settings file you'll see all the documentation on what each setting does and how to manipulate it.

All the settings are there: fileModel, fileVar, uploadDir, allowedTypes, fields, automatic, and massSave (new since 3.6)

API changes...


Due to the new way we handle our settings globally, setting and retrieving our settings has slightly changed. The full migration guide is included in the most recent version of this plugin (file_upload/migration_guide_3_x-4_0.txt), but I'll go over the change briefly as its very straight forward.

GETTING YOUR SETTINGS:


OLD WAY:
  1. $this->FileUpload->fileModel;
  2. $this->FileUpload->fileVar;
  3. $this->FileUpload->fields;

NEW WAY:
  1. $this->FileUpload->fileModel();
  2. $this->FileUpload->fileVar();
  3. $this->FileUpload->fields();


As you can see, each setting has been given its own method (actually its a __call overload so you can create your own settings if you'd like). By not passing in any parameters to the setting's method it will return the current value of that setting. If you then pass in a value to the setting's method you'll then be setting that setting (that's fun to say). I think an example will help explain:

SETTING YOUR SETTINGS:


OLD WAY:
  1. $this->FileUpload->fileModel = null;
  2. $this->FileUpload->fileVar = 'file';
  3. $this->FileUpload->allowedTypes = array('application/pdf');

NEW WAY:
  1. $this->FileUpload->fileModel(null);
  2. $this->FileUpload->fileVar('file');
  3. $this->FileUpload->allowedTypes(array('application/pdf'));


As you can see, by passing in a value to that setting's method you'll now be setting that specific option.

Legacy Plugin: I've stamped the latest version without these API changes and without the behavior or global settings to 3.6.3. I will be keeping this legacy plugin for those who wish to stay with the old way of doing things. Get the legacy plugin v3.6.3 here: http://projects.webtechnick.com/file_upload_plugin_v3_6_3.tgz

Behaviors for the WIN!


New in 4.0 is a great behavior to attach to your model. By using this behavior you will no longer need the component. I've left the component in, for legacy reasons, as well as its the only way to preform file uploads without a model (one of my main goals when developing this project).

The behavior is extremely flexible and easily fits into your current project allowing for automagic file uploading without you having to think about it. Setup is simple:

Attach the FileUpload.FileUpload to your model of choice, we'll use our trusty Upload model as an example...
  1. class Upload extends AppModel {
  2.   var $name = 'Upload';
  3.   var $actsAs = array('FileUpload.FileUpload');
  4. }


Simple as that, now whenever you (or another associated model) save an upload with a file submitted, a record will be saved and all your file related columns (name,size,type) will be filled in for you automatically. The plugin takes special care not to overwrite files.

As discussed before, all the settings will be inherited by the new settings class located in /plugins/file_upload/config/file_upload_settings.php, but that doesn't mean you can't pass in your own settings on the fly. Using our same Upload model, lets change the save directory to 'uploads', our column names from name,size,type to file_size,file_size,file_type, lets only allow .pdfs to be uploaded, lets make it so an upload is required (required is new in 4.2.x), and only allow less than 10KB files (new in 4.3).
  1. class Upload extends AppModel {
  2.   var $name = 'Upload';
  3.   var $actsAs = array(
  4.         'FileUpload.FileUpload' => array(
  5.           'uploadDir' => 'uploads',
  6.           'fields' => array(
  7.             'name' => 'file_name',
  8.             'type' => 'file_type',
  9.             'size' => 'file_size'
  10.           ),
  11.           'requilred' => true,
  12.           'maxFileSize' => '10000',
  13.           'allowedTypes' => array('application/pdf')
  14.         )
  15.       );
  16. }


All set, all the default settings are the same except we're now uploading to the 'uploads' directory (app/webroot/uploads), we've changed our file specific columns from name, type, and size, to file_name, file_type, and file_size, we require a file to be uploaded, our max file size to allow is 10,000 bytes, and finally we're only allowing .pdf files to be uploaded and saved as new records.

Doing the upload is just as simple in the view, here's an example:
  1. echo $form->create('Upload', array('type' => 'file'));
  2. echo $form->input('Upload.file', array('type' => 'file'));
  3. echo $form->end('Save');



Doing your file uploading this way with a behavior lets you build your associations with full upload support without hesitation. Continuing with our example lets say we have an Application model that hasMany Uploads.

Assuming Application->hasMany->Uploads, in views/applications/add.ctp :
  1. echo $form->create('Application', array('type' => 'file'));
  2. echo $form->input('Application.name');
  3. echo $form->input('Upload.0.file', array('type' => 'file'));
  4. echo $form->input('Upload.1.file', array('type' => 'file'));
  5. echo $form->end('Save Application and Two Uploads');


Notice we're just using normal cakePHP association syntax in our view, just be sure to use saveAll() instead of save() in your application controller add function. =)

Automagic File Upload Deletions...


The new behavior will automatically delete the source file when the record of that upload has been deleted. No more worrying if you have old crusty files hanging around your file system long after any record of it ever exists. =)

That's it, enjoy the new features!


That's it for version 4.0. There's more to look over in the API and in the actual code itself if you'd like to learn more.

API: http://projects.webtechnick.com/docs/file_upload_plugin

Thanks, I hope you all find it useful! As always, if you like the plugin, find a bug, or have a feature request -- post a comment. =)

Nick

Comments

11/30/2009 10:02 am

change to checkType

with this change you can set 'allowedTypes' => array('*') to don't restrict file types

function checkType($file = null){
$this->setFile($file);
foreach($this->options['allowedTypes'] as $value){
if(strtolower($this->file['type']) == strtolower($value) || $value == '*'){
return true;
}
}
$this->_error("Uploader::_checkType() {$this->file['type']} is not in the allowedTypes array.");
return false;
}
11/30/2009 7:41 pm

Thanks for the contribution

I've included it in the repository.

To allow all upload mime types you can use
  1. $this->FileUpload->allowedTypes(array('*'));


Hope all is well,
Nick
12/02/2009 10:53 pm

Resize

Hi, thank you for your great plugins.

Is there any chance to resize the image and also create a thumbnail while uploading it. If it supports, how can I do that? I'm using the behaviour uploading and not the component one.

Cheers.

Amirul.
12/03/2009 5:05 am

Hi Amirul

Thank you for the kind words, unfortunately there isn't a native way to auto resize an image. However with a little afterSave magic you can do it yourself using the built in plugin. This isn't the cleanest way but it should work.

  1. /*Add into your model, I'm assuming your file_name database column is 'name' (default), if it isn't you'll need to change it in the function.*/
  2. function afterSave($created){
  3.   $resize_width = 100;
  4.  
  5.   if($created){
  6.     if(!empty($this->data[$this->alias]['name'])){
  7.       App::import('Helper', 'FileUpload.FileUpload');
  8.       $FileUpload = new FileUploadHelper();
  9.      
  10.       $FileUpload->image($this->data[$this->alias]['name'], array('width' => $resize_width, 'imagePathOnly' => true));
  11.     }
  12.   }
  13. }


That will resize the uploaded image and save the resized image to your resized directory as specified by the helper.

I'll add your request to the *wish list* for future releases.

Hope all is well,
Nick
12/03/2009 6:42 pm

Helper not helping

Thanks for your prompt reply.

I did what you have suggested but don't really work. I got this error after I tried to upload/save it:

Warning (4096): Argument 1 passed to Upload::afterSave() must be an instance of boolean, boolean given, .....

Warning (2): Cannot modify header information ...

I'm not sure where I did wrong but for some reasons the images didn't save to the 'resized' directory (the 'resized' dir has set chmod 777).

FYI, I have also tried what you have suggested on the image resizing comments at http://www.webtechnick.com/blogs/view/221/CakePHP_File_Upload_Plugin It also not working for me. It seems that the helper don't do the job. Do you have any suggestions where am I missing?

Thanks a lot.
12/04/2009 1:22 am

chmod

I've just checked the chmod for the uploaded file. It seems that the uploaded file has chmod to 600 for some reasons. Is that makes sense why the helper couldn't resize the image?
12/04/2009 8:12 am

Images should be 644.

That's possible. If the helper can't read the image to create a new one based off of the image, it won't be able to resize it using the GD library (it will just use browser based resize instead).

Can you view the image normally in your browser?
12/09/2009 12:30 pm

Some hacks

Hey there - I'd like to thank you for this plugin, its been working for me on a couple of projects. I have made some hacks (which tend to be fairly ugly hacks):

1) I needed support for hasOne relationships - where a model only had one image lets say - so I added some logic to the behavior to find and delete all Uploads associated with a foreign_key before saving. This was tricky and my code isn't that great, I'm sure you might be able to figure out a better way off adding this feature in.

2) I also like to keep width/height information in my tables so I can add that information to the HTML directly, so I added some logic to Class Uploader at line 90:

//now move the file.
if(move_uploaded_file($this->file['tmp_name'], $target_path)){

// Get image metadata
$meta = @getimagesize($target_path);
$this->finalFile = basename($target_path);

$output['height'] = $meta[1];
$output['width'] = $meta[0];
$output['meta'] = $meta[3];
$output['filename'] = $this->finalFile;

return $output;
}
12/09/2009 9:31 pm

Thanks

Cool, glad you got it working the way you wanted, and thanks for sharing. =)

You might consider moving that code into a beforeSave() callback in your upload model that set the height/width/meta keys in your $data array before the final save. That would make you're needed code separate from the plugin (allowing for easy upgrades and such). Just something to consider.

Thanks for sharing, and the kind words. =)
02/16/2010 5:39 am

abhishek chowla

hey nick thanks for this gr8 post & plugin.. i m new to cakephp and i am using this plugin for mutiple file upload and resizing... but i m stucked,
my images are getting stored on server with original size and resized. but it throws me error as mentioned below


Warning (4096): Argument 1 passed to Upload::beforeSave() must be an instance of boolean, array given, called in /opt/lampp/htdocs/abhishek/projlibrary_cake/cake/libs/model/model.php on line 1193 and defined [APP/models/upload.php, line 7]

Code

var $actsAs = array('FileUpload.FileUpload'=> array('uploadDir' => 'files'));

function beforeSave(boolean $created)

Upload::beforeSave() - APP/models/upload.php, line 7
Model::save() - CORE/cake/libs/model/model.php, line 1193
Model::__save() - CORE/cake/libs/model/model.php, line 1658
Model::saveAll() - CORE/cake/libs/model/model.php, line 1562
FileUploadComponent::processFile() - APP/plugins/file_upload/controllers/components/file_upload.php, line 291
FileUploadComponent::processAllFiles() - APP/plugins/file_upload/controllers/components/file_upload.php, line 347
FileUploadComponent::startup() - APP/plugins/file_upload/controllers/components/file_upload.php, line 184
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 89

Notice (8): Undefined property: FileUploadHelper::$Html [APP/plugins/file_upload/views/helpers/file_upload.php, line 270]

Code | Context

$image = "/files/resized/mapdatax100.gif"
$options = array()

}
else {
return $this->Html->image($image, $options);

FileUploadHelper::_htmlImage() - APP/plugins/file_upload/views/helpers/file_upload.php, line 270
FileUploadHelper::_getImageByName() - APP/plugins/file_upload/views/helpers/file_upload.php, line 211
FileUploadHelper::image() - APP/plugins/file_upload/views/helpers/file_upload.php, line 94
Upload::beforeSave() - APP/models/upload.php, line 21
Model::save() - CORE/cake/libs/model/model.php, line 1193
Model::__save() - CORE/cake/libs/model/model.php, line 1658
Model::saveAll() - CORE/cake/libs/model/model.php, line 1562
FileUploadComponent::processFile() - APP/plugins/file_upload/controllers/components/file_upload.php, line 291
FileUploadComponent::processAllFiles() - APP/plugins/file_upload/controllers/components/file_upload.php, line 347
FileUploadComponent::startup() - APP/plugins/file_upload/controllers/components/file_upload.php, line 184
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 89


Fatal error: Call to a member function image() on a non-object in /opt/lampp/htdocs/abhishek/projlibrary_cake/app/plugins/file_upload/views/helpers/file_upload.php on line 270

so please give some time and le me know the solution, thanks in adv
02/17/2010 3:27 am

Email me your code

Thanks for the kind words. I find it much faster to debug what's going on if you email me your code view/controller/model.

At first glance it looks like you're using the component and the model behavior at the same time. You should use one or the other as using both will cause collisions and duplicate records.

Hope that helps,
Nick
02/19/2010 4:39 am

Using plugin inside a form related to a different model (not upload.ctp)

Dear Nick,

I would like to ask you if/how it is possible to use your great plugin inside a form related to a different model (not upload.ctp) by using saveall()...

Let me show you a simplistic case I'm experimenting on:

I have two database table:

CREATE TABLE IF NOT EXISTS `posts` (
`id` int(11) unsigned NOT NULL auto_increment,
`title` tinytext NOT NULL,
`created` datetime default NULL,
`updated` datetime default NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

CREATE TABLE IF NOT EXISTS `uploads` (
`id` int(11) unsigned NOT NULL auto_increment,
`name` varchar(200) NOT NULL,
`post_id` int(11) 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=1 ;

where post-HASMANY-upload and upload-BELONGSTO-post

So here are the two models:

app/models/post.php

class Post extends AppModel {
var $name = 'Post';

var $hasMany= array('Upload');
var $validate = array(
'title' => array('rule' => array('notEmpty'))
);
}
?>

app/models/upload.php

class Upload extends AppModel {
var $name = 'Upload';
var $actsAs = array('FileUpload.FileUpload');

var $belongsTo = array('Post');

var $validate = array(
'name' => array('rule' => 'notEmpty')
);
}
?>

Now, if possible, I would like to have a form where in the view action add.ctp for the post model i also include the uploading field... to achieve this goal I have try with the following posts_controller.php and add.ctp view action:

app/controllers/posts_controller.php

class PostsController extends AppController {

var $name = 'Posts';
var $helpers = array('Html', 'Form');


function index() {
$posts = $this->paginate();
if(isset($this->params['requested'])) {
return $posts;
}
$this->set('posts', $posts);
}

function add() {
if(!empty($this->data)) {
$this->Post->saveAll($this->data, array('validate'=>'first'));
}
}
}
?>

app/views/posts/add.ctp


create('Post');?>


input('title'); ?>


//echo $form->create('Upload', array('type' => 'file'));
echo $form->input('Upload.name');
echo $form->input('Upload.0.file', array('type' => 'file'));
echo $form->input('Upload.1.file', array('type' => 'file'));
//echo $form->end('Save Upload and Two Uploads');
?>

end('Submit');?>




  • link(__('List Posts', true), array('action' => 'index'));?>

  • link(__('List Users', true), array('controller' => 'users', 'action' => 'index')); ?>

  • link(__('New User', true), array('controller' => 'users', 'action' => 'add')); ?>




Like this I don't get any particular errors, but only the following partial results:

- posts data are all saved correctly to the database
- for uploads, only the field 'name' is saved (with '1' as prefix !?!)
- no photos are actually uploaded to the proper location app/webroot/files

This is it... what do you think ? Can you maybe see any evident wrong sides in this method ? Do you think it's praticable in a different way ?

Thanks a lot in advance for any useful advice


Best Regards


deepz
02/19/2010 5:07 am

Looks like you're not sending the file with the upload

It looks like you forgot to add array('type' => 'file') in your $form->create('Post') call. You also do not need the Upload.name field, the behavior will populate that for your automatically. Keep with the cakePHP conventions and your form should look like this:

//add.ctp
  1. <?php
  2. echo $form->create('Post', array('type' => 'file'));
  3. echo $form->input('Post.title');
  4. echo $form->input('Upload.0.file', array('type' => 'file'));
  5. echo $form->input('Upload.1.file', array('type' => 'file'));
  6. echo $form->end('Submit');
  7. ?>


Good luck,
Nick
02/19/2010 5:20 am

works perfectly !

You're obviously right... and it works perfectly !

Thanks a lot Nick !

deepz
02/21/2010 1:29 pm

Duplicate record

First, your plugin is awesome. I am trying to save multiple files for an application. I have the model set as Application->hasMany->Upoads, and I am trying to use the magic autosave MassiveSave. However, it creates two records in my application. I thought it should create one record and two files in upload. Am I missing something? I have read the doc.
Thanks
02/21/2010 2:53 pm

Email me your code

Email me your code so I can point you in the right direction.

Most likely you're calling $this->save in your controller when you don't have to (if you're using the component). Or you don't have your form setup correctly (if you're using the behavior approach). Or both. Hard to tell you what's going on exactly without seeing what you're doing.

Hope that help,
Nick
02/21/2010 4:44 pm

This is what i have

var $helpers = array('Html', 'Form', 'FileUpload.FileUpload');
var $components = array('FileUpload.FileUpload');


function beforeFilter() {
parent::beforeFilter();

$this->FileUpload->uploadDir('files/publications');
$this->FileUpload->fields(array('name'=>'name', 'type' => 'type', 'size' => 'size'));
$this->FileUpload->fileModel('Upload');
//$this->FileUpload->allowedTypes(array('application/pdf'));

$this->FileUpload->massSave(true);
}


function add() {
if (!empty($this->data)) {
//this->Publication->create();

if($this->FileUpload->success ){
//$this->Session->setFlash('Save Successfull!');
//$images = $this->FileUpload->uploadIds;
//$this->Publication->saveField('upload_id', $images[0]);
$this->Session->setFlash(__('The Publication has been saved', true));
$this->redirect(array('action'=>'index'));
}
else { $this->Session->setFlash($this->FileUpload->showErrors());
}
}
}

In add.ctp, I have:

?php echo $form->create('Publication', array('enctype' => 'multipart/form-data'));?>


echo $form->hidden('Publication.date');
echo $form->input('Publication.title');
echo $form->input('Publication.description');
echo $form->input('Publication.location');
?>




//echo $fileUpload->input();
echo $form->input('Upload.0.file', array('type' => 'file'));
?>




//echo $fileUpload->input();
echo $form->input('Upload.1.file', array('type' => 'file'));
?>


end('Submit');?>
02/21/2010 5:08 pm

Looks correct, but use behavior if using model.

That looks correct. Do you have any before/after Saves in your Application or Upload model?

If you're using a model, I suggest using the behavior instead of the component (the component is depreciated and is only kept in the plugin to allow for non-model use).

For your configuration I'd add this to your Upload model
  1. var $actsAs = array(
  2.     'FileUpload.FileUpload' => array(
  3.       'uploadDir' => 'files/publications'
  4.     )
  5.   );


Then just remove the FileUpload.FileUpload from your components array and change your add action to something like this:

  1. function add(){
  2.   if(!empty($this->data)){
  3.     if($this->Application->saveAll($this->data)){
  4.       $this->Session->setFlash('Success!');
  5.     }
  6.     else {
  7.       $this->Session->setFlash('Failure!');
  8.     }
  9.   }
  10. }


Keep your view the way it is. That should get you what you expect.

Hope that helps,
Nick
02/21/2010 5:43 pm

Nice!! It works fine now

Thanks Nick!! It works perfectly now. So it looks like we don't have to do $this->FileUpload->massSave(true) anymore when using behavior. Thanks again!!
02/22/2010 3:03 am

Excellent plugin

Hi Nick, great work.
Behaviour works like a charm. I was able to use it with my model that has quite a few other properties (besides the required 3).

Any tips on how to use the bundled RResizeImage class? I see it's interface in docs, but how to use it within CakePHP framework (and/or FileUpload module)?

Just can't figure out how to start.
02/22/2010 3:41 am

RResizeImage is a utility for FileUploadHelper

You get at the RResizeImage via the FileUploadHelper. The FileUploadHelper takes advantage of the RResizeImage class to auto resize your images on the fly.

If you want direct access to the RResizeImage class from somewhere else in your app you'll have to import FileUploadHelper and then instantiate it.

  1. App::import('Helper','FileUpload.FileUpload');
  2. $ResizeImage = new RResizeImage();


That should get you what you want. But like I said, I don't suggest using it directly. Let the FileUploadHelper do the work for you on the fly.

Thanks for the kind works,
Nick
02/22/2010 3:57 am

Thanx

Thanks Nick.
But can i use the helper to serve thumbnails (really) on the fly, without saving them on server? I'll need various thumb sizes in various parts of the app., so i don't want to save all the sizes to disk, espec. since the sizes' cpecs might change in the future.
02/22/2010 5:05 pm

Use Browser resizing if filesystem is not an option.

I don't quite understand what you'd like. The idea of using the GD library or any other thumbnail resizing utility is to save the result somewhere so you don't hit the same time/resource expenditure on each request with a large original image. If you don't save that result to the filesystem or database (ick!), all you're doing is forcing the same logic/resources to take place the next request... slooooow.

If you (really) don't want to save the resulting thumbnail to disk I suggest just use the browser to resize your photos on the fly. Your users will still have to download the large file, but they wont have to wait for a GD library resize of the file on top of that request.

  1. echo $html->image('large_photo.jpg', array('width' => '50'));


The primary feature of the FileUploadHelper is the image function, it will resize your photo and save it to your filesystem for immediate retrieval 'next-time'. Your first request might take a second or two, but the next one will be a fraction of that.

  1. echo $fileUpload->image('large_photo.jpg', array('width' => '50'));


Hope that helps,
Nick
02/23/2010 3:52 am

it's not really the same

Thanx again for your answer.
I've tested, and browser-resizing (i.e. loading original images) is about 5 times slower than getting a GD generated thumbnail (with no caching).
The thing is, sometimes i'm building sites which use thumbnails in teh site, and then i do create thumb files. But sometimes the site itself doesn't need thumbs; i only need them in CMS (for more user-friendly image-maintenance), so i wanted to avoid creating thumbnails on server when they will be very rarely accessed, and only by the site admin. That's why i wanted on-the-fly generation.
But be that as it may, when i try your solution, it doesn't work. The afterSave function you suggested above doesn't create any thumbnails, and "echo $fileUpload..." doesn't output anything :/
It's not totally clear to me how to use it. In afterSave function the image function seems void (i.e. the output is not assigned to any var), but it does have a 'imagePathOnly' option!? While in your last example it seems to generate a tag.
If you could explain how to use it exactly, you would help me a lot. I'm not (usually) the type who asks for help from class authors, but it's now the second day i'm losing just to get thumbnails shown in ANY way (i've tried 2 other solutions besides your class but to no avail)
Sorry to bother you, but i'm startinig to pull my hair out over this.
Thanks in advance.
02/23/2010 3:55 am

nota bene...

...i've done thumb generation many times, but this is the first time i'm trying to do it within cake framework. Needless to say, this is my first cakephp project.
02/23/2010 4:56 am

Helpers are typically exclusive to views.

I'm not sure how that's the case -- that reading the image and resizing it via the GD library can be faster than just reading the image, but if say so.

In terms of using the Helper in an afterSave. The afterSave example is not a typical use of any helper, it was an example of how to create a resized image immediately after the user submits an image -- to bypass the slight performance hit the next time (ie first time) a user wishes to view that image smaller.

However, that's not the typical use. Typically a helper is used exclusively in views. Those examples in my previous comment are code snippets in a view, not the afterSave() function in a model.

I can see why you might be confused, but I hope that clears things up. The imagePathOnly option is helpful for javascript/ajax features that require the path of a generated thumbnail, as such an img tag isn't produced. In the case of the afterSave() method we don't care about the return value, only that the resizing happened as a byproduct of calling $FileUpload->image(...).

In the end its up to you how you want to handle your thumbnails. I've always found that diskspace is cheap, ram/cpu is expensive in the hosting world.

Hope that helps,
Nick
02/23/2010 5:14 am

Digging through past blogs

Digging through my other blogs on the subject I posted specifically about the FileUploadHelper. This might help you out more.

http://www.webtechnick.com/blogs/view/205/2_0_1_FileUploadHelper_Released

There are also more examples of view/controller/model relationships via the more generic Plugin blog here:

http://www.webtechnick.com/blogs/view/221/CakePHP_File_Upload_Plugin
02/23/2010 9:43 am

Error for Max Size

I have the behavior uploading just fine but the error message for max file size does not come, it just forwards it back to the same page with no display, no successful flash or even my generic failed to save. Ideas?
02/23/2010 10:26 am

Checking out

Thanx Nick for the effort. I'm checking it out.
In the meantime, i've managed to have afterSave callback work, but when i try to echo/output the tag, upload helper "claims" it's Html helper is null, be it as $this->Html or $html var. Strange, it's listed in $helpers array, and it doesn't help even if i App::import it literally.
I'll keep you posted when i figure out what may be wrong.
PS. Re GD etc: reading large image + generating a thumb may take a few ms longer than just reading, but 2 Kb still takes a lot less to _travel_ than 700 Kb :).
02/23/2010 6:00 pm

Validation new in 4.1.0

Validations errors are new in 4.1.0. Make sure you have the latest FileUpload Plugin for validation errors to display. Also make sure to pass 'validate' => 'first' into your saveAll() function to tell cake to be sure all validates pass before anything is saved.

  1. if($this->Model->saveAll($this->data, array('validate' => 'first'))){
  2.   //YAY!
  3. }
02/23/2010 6:06 pm

afterSave()

Indeed, if you want to output an image tag in an afterSave() (not sure why) you'll have to build what the helper expects as you are not creating the helper the way a controller would create the helper when passing it into a view.

  1. //in an afterSave() function in a model
  2. //This is not the typical use of a helper....
  3. App::import('Helper', 'FileUpload.FileUpload');
  4. App::import('Helper','Html');
  5. $FileUpload = new FileUploadHelper();
  6. $FileUpload->Html = new HtmlHelper();
  7.  
  8. echo $FileUpload->image('some_image.jpg');


Again, I don't recommend doing this for everyone else following the comments. Use the FileUploadHelper the way it was meant to be used -- in a view. =)
02/24/2010 4:06 am

not echoing in model but in view

i'm not trying to echo in the afterSave, but in the view.
afterSave is just the 1st of 2 things that i succeeded in making work.
Thanx for the tip to explicitly instantiate Html helper (and not only import). I think i read somewhere that even just listing a helper in $helpers array should be enough to have a instance available; same with App::import-ing one; but apparently it's not so.
So now that i did instantiate Html helper, i have it output the tag finally - but with a small bug:

it was outputing e.g.
img/\img\resized\imagenamex150.jpg

instead of /img/resized/imagenamex150.jpg
(as you may notice, i'm developing locally on Win machine)

so after i fixed that in my view (didn't want to tamper with your helper in case there'll be upgrades), i have it finally working.
I've now removed the whole afterSave part in the model, as everything now works ok without it.

Thanx again for your help and sorry for lost time! :)
You're a great guy, God bless you.
02/24/2010 4:39 am

You found a bug. Thanks!

Ahhh, you know I think you may have found a bug. Doing some digging, I'm using DS as document separator but it really should just be '/' because regardless of the server the end result needs to be /files/.... On either Linux or Windows.

I'll post a patch soon after this post.

In regards to the Helpers array you should indeed only have to add FileUpload.FileUpload into the helpers array of your controller.

  1. //some_controller.php
  2. var $helpers = array('FileUpload.FileUpload');


Doing that will grant access to $fileUpload in a corresponding view that will have Html instantiated for you already. You only have to do it by hand if you're App::import'ing FileUploadHelper by hand. Hope that makes sense.

Thanks for the catch on the windows server. I'll patch that ASAP.
02/24/2010 4:44 am

4.1.1 Fixed Windows Server Image Paths

version 4.1.1 should fix your windows server image path issues. Let me know if you have any more issues.

Thanks again for the catch,
Nick
02/24/2010 4:52 am

there's two parts to the bug...

...i had.
First is indeed the DS version, and second is that the html helper was adding the default img/ to the front of the $image path, which already had it's /img/ part added (i guess coming from options['uploadDir'])
02/24/2010 5:05 am

Hmm...

So you've set your uploadDir to 'img' and its adding a duplicate '/img' to your path?

I'll have to look into that.

By default the files will be uploaded to /webroot/files/ to keep separation between your website /img and uploaded files.
02/24/2010 5:14 am

exactly

Yes, it (fileUpload helper's Html helper) is adding an additional img/ to the front.
I have upload dir set to img because i'm using it in CMS, to add images to the site/page.
03/02/2010 5:24 am

resize not working

hi nickup
i have used component method 4 uploading files and files are getting saved to desired folder correctly .But when im trying to tresize them using afterSave it doesno works i.e. file is not resized and not saved in resized folder.
03/02/2010 6:02 am

What does your afterSave() function look like?

What are you doing in your afterSave() function to resize the uploaded image? Feel free to email me your code and I can point you in the right direction.
03/08/2010 9:22 am

Validation

Yes I have the latest 4.1
my save looks like this

if($this->Upload->saveAll($this->data,array('validate' => 'first'))){ //stuff }

When i try to do a save with a file type not allowed I get a warning at the top
Warning (512): Uploader::_checkType() application/vnd.openxmlformats-officedocument.spreadsheetml.sheet is not in the allowedTypes array. [APP/plugins/file_upload/vendors/uploader.php, line 133]

When I try a file that is too big, nothing even happens, just forwards back to the page with no status messages.

not sure how to debug this.
03/09/2010 5:19 am

Validation errors only work when using the Behavior

So you'll get nice validation errors if you are using the Behavior instead of those ugly warning messages that will disappear if you turn debug to zero.

However, you should be receiving the UPLOAD_ERR_INI_SIZE (int of 1) error if your file is outside the range of what your PHP configuration allows.

To debug I suggest putting a debug($this->data) before your save to see what error code PHP thinks its getting.

Good luck,
Nick
03/09/2010 7:22 am

RE: Validation errors only work when using the Behavior

Its not even getting back to the controller if the file is too big. Must mean its in the uploader that its failing and not returning a value.
03/15/2010 7:29 am

Love it

Excellent plugin. Extremely helpful. Got most of the things that I wanted from a file upload.

I have only one issue now, my upload table contains 5 fields and during multi-upload I want to specify an additional comment for that particular row.

I can't seem to find a way to achieve that. I got the reference column to the master database through association. Is there a way for me to be able to add additional columns to the model?
03/18/2010 8:31 pm

Thanks for the kind words

Yes you can certainly set other keys on the uploaded file that is being passed in.

An example of this might be:
  1. $form->create('Model', array('type' => 'file'));
  2. $form->input('Upload.0.file', array('type' => 'file'));
  3. $form->input('Upload.0.comment');
  4. $form->input('Upload.1.file', array('type' => 'file'));
  5. $form->input('Upload.1.comment');
  6. $form->input('Upload.2.file', array('type' => 'file'));
  7. $form->input('Upload.2.comment');
  8. $form->end('Submit');


The above example will save the comment with its associated file upload.

Hope that helps,
Nick
03/21/2010 12:35 pm

Validation

Hi Nick,

Is there a way to only validate the file? I mean, there is no saving after it validates like $this->Model->validates(). Because i want to use ajax to check the file first if it validates and display a check beside the file field.

03/21/2010 5:33 pm

Can't send files through Ajax

In regards to validation, $this->Upload->validates() should give you want you want if you have a file in your $this->data the behavior does have a beforeValidation function that checks for various errors and returns false upon an error.

Unfortunately, a user will have to submit the file, because you cannot send a file through an XHR (ajax). You can mimic ajax with iframes or flash, but it's not ajax, and you're still sending the file. It's a limitation of javascript.

I know a lot of developers complain about that limitation, but I'm happy its there. Otherwise, nothing would stop developers from creating scripts that could scan our hardrives looking for specific files and upload them without us knowing. I for one am glad Javascript cannot open and send files without human interaction. :)

Hope that helps,
Nick
03/22/2010 12:33 am

Ajax

Hi Nick,

I'm not going to send it through ajax. I just want to use ajax in validation, no saving yet.

By the way, how can I set the max size of the file?

Thanks,

Maurelle Mejos
03/22/2010 9:15 pm

@Maurelle

You can set the upload_max_filesize to whatever you want in your php.ini (if your host allows it).
03/22/2010 9:30 pm

Filesize limit

You can't do that in a shared hosting.

In my previous project, I was able to restrict every users to upload only images that does not exceed 1mb by checking the size in the file array.

I hope you will include this in your next version Nick. This is very important to prevent our users from uploading huge files.

Thanks,

Maurelle Mejos
03/23/2010 12:35 am

Version 4.3 update

Good suggestion Maurelle. As such, I've created a new setting called 'maxFileSize' that you can set to limit the filesize you allow your users to upload. 'maxFileSize' is set like any other setting; through the behavior, controller, or file_upload_settings.php.

I suggest using it like so:
  1. var $actsAs = array(
  2.   'FileUpload.FileUpload' => array(
  3.     'maxFileSize' => '10000' //bytes ~10KB
  4.   )
  5. );


Have fun and thanks for the suggestion,
Nick
03/23/2010 2:32 am

up to .ini restriction or above?

can this new setting allow the users to upload files larger than max_file_size in php.ini?
or only up to this limit?
03/23/2010 2:52 am

maxFileSize up to .ini restriction

maxFileSize will not bypass the max_upload_filesize in php.ini. You can only go as high as max_upload_filesize allows.

max_upload_filesize is a PHP limitation, whereas maxFileSize is a CakePHP Validation limitation.

Hope that helps,
Nick
04/10/2010 7:38 pm

no errors but file did not upload

hi nick, i'm using cakephp ver 1.2.6 and use your FileUpload Plugin v3.5, these are step that i am doing to implement your plug-in

- create database table

CREATE TABLE IF NOT EXISTS `filedownloads` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title_ina` varchar(255) NOT NULL,
`title_eng` varchar(255) DEFAULT NULL,
`body_ina` text NOT NULL,
`body_eng` text,
`filedownloadtype_id` int(11) NOT NULL,
`name` varchar(255) DEFAULT NULL,
`type` varchar(255) DEFAULT NULL,
`filesize` int(11) DEFAULT NULL,
`created` datetime DEFAULT NULL,
`modified` datetime DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;

- copy file_upload.php to app/model/behaviors
- copy file_upload.php to app/controller/component
- copy uploader.php to app/vendors

/*this is my model*/
class Filedownload extends AppModel {

var $name = 'Filedownload';
var $validate = array(
'title_ina' => array('notempty'),
'body_ina' => array('notempty'),
'filedownloadtype_id' => array('numeric'),
'name' => array('notempty')
);

//The Associations below have been created with all possible keys, those that are not needed can be removed
var $belongsTo = array(
'Filedownloadtype' => array(
'className' => 'Filedownloadtype',
'foreignKey' => 'filedownloadtype_id',
'conditions' => '',
'fields' => '',
'order' => ''
)
);

var $actsAs = array(
'FileUpload.FileUpload' => array(
'uploadDir' => 'files',
'fields' => array('name' => 'file_name', 'type' => 'file_type', 'size' => 'file_size'),
'allowedTypes' => array('application/pdf'),
'required' => true, //default is false, if true a validation error would occur if a file wsan't uploaded.
'maxFileSize' => 'false' //bytes OR false to turn off maxFileSize (default false)
)
);

}
?>

and this is my filedownloads/add.ctp file


create('Filedownload', array('type' => 'file'));?>


echo $form->input('title_ina');
echo $form->input('title_eng');
echo $form->input('body_ina');
echo $form->input('body_eng');
echo $form->input('filedownloadtype_id');
echo $form->input('name');
echo $form->input('Upload.0.file', array('type' => 'file'));
echo $form->input('type');
echo $form->input('filesize');
?>

end('Submit');?>



  • link(__('List Filedownloads', true), array('action' => 'index'));?>

  • link(__('List Filedownloadtypes', true), array('controller' => 'filedownloadtypes', 'action' => 'index')); ?>

  • link(__('New Filedownloadtype', true), array('controller' => 'filedownloadtypes', 'action' => 'add')); ?>




everything is working fine but the file did not upload to my server, is there anything wrong? , please need your guide



04/12/2010 2:25 am

No need to copy to behaviors/components/helpers

Hi,

There is no need to copy the related files to your app. The plugin is self containing (like all CakePHP plugins. Simply drop the 'file_upload' directory into your /app/plugins/ directory and then proceed with your steps.

Hope that helps,
Nick
07/07/2010 12:29 am

the resized folder

when i use echo $fileUpload->image($name, array('width' => 100));
does the resized folder gets created own its own??

if a create the folder manually then also the resized image does not gets created.

What am i missing?
07/08/2010 3:29 am

Resized folder does not get created on its own

The resized folder needs to be within the uploadDir (default is webroot/files/resized) and it needs to be writable by the plugin.

The resize element of the plugin needs to have the GD library installed to work, otherwise the fallback is browser resizing (ie setting width/height to what it would have been).

Hope that helps,
Nick

Add Comment

Please login or register to submit a comment.