Generic File Upload Behavior

14 September, 2007

Show comments

On a recent project I had to address the requirement that the admin user can "upload any type of file and associate it with any of the models in the system". In reality this seems to be a rather logical and common requirement. For quite a while now I've been collecting bits of info on how to upload files with cake and it's about time to begin sharing the knowledge. Did I say sharing? I meant I'll show you what I know, and hope you show me how to do it better :)

Partly because I am really loaded at the moment, but also due to the number of files which are 'supporting' the behavior are numerous, this blog will not follow the format of my previous posts. The code which to be discussed is in the noswad CakeForge project


A massive shout out to both Tane Piper (Digitalspaghetti) for the original upload behavior and Jon Bennet (JBen) for his insightful Image helper which, as you may note from the download, forms the basis for the image manipulation code in the ImageUpload behavior.

The code has been taken from a running project, but I have not yet commented the code due to time constraints but in any case the code may change - Particularly as the code relies on the patch attached to Ticket 3178 to work.

I'll write a tutorial on the bakery if the code settles down after taking on board whatever suggestions come forth.

What's In it?

The Upload Behavior

There are a lot of files in the download but the most important inclusion is the upload behavior.

This file allows you to save meta data about your file uploads in the database and store the files wherever you configure the uploaded files to go.

Control of uploads by mime type, extension and size

Validation related to file uploading itself is handled by the behavior (dependent on ticket 3178).

The behavior will only be active for file uploads, as such if you wanted to edit the meta data that is collected you can via a normal form.

Image Upload Behavior

This file demonstrates clear separation from file upload handling and image manipulation. In addition to everything it inherits from the upload behavior it also saves width and height data to the database (meta) table if the fields are present and has two functions defined to permit resizing of a file that has been uploaded or any arbitrary image file which it is passed. After resizing a file either the file is written to the path defined in the parameters, or the image contents are returned to the calling method.

Generic App Model

The associations defined in this file permit a thumb image to be associated with any model instance, and any number of files in general to be associated and used as you wish.

Attachment administration controller and views

Lets you upload files and by default stores files in APP/uploads. Files are not directly stored in the webroot.

Using the attachment controller, you can add and view all files that are uploaded.

The model and id to which the attachment will be associated is dependent on the url parameters passed. To upload a file for Blog number 22 you would go to the url /admin/attachments/add/Blog/22. Submit the form and it's up there for use.

File and Image handling controller

The functionality within this controller could just as well be put in the attachments controller, but it is presented here separately.

The routes file that is in the download will route requests for /files/* and /img/* to this controller. On the fly image resizing is also built into this controller. "On the fly image resizing?" you say? Consider the following example, see the source for more info:

  • As an admin user you go to the url /admin/attachments/add/Blog/22 and upload a file named "test.jpg" and give it a description of "what happens here". The file uploaded is 3000px by 5000px. Users :D!
  • You go to, or include in a page, the url /img/Blog/22/test-what-happens-here.jpg . The image you would see in this case is served at the default size, which if you don't change the code means you will see your image restricted to 300px wide and 1000px high, as you don't want it changing size (most likely) what you will therefore get is your image served as 300px x 500px.
  • You want to have a thumb of the same image so you go to, or include in a page, the url /img/50x50/Blog/22/test-what-happens-here.jpg. Tada, you have your thumb.
  • You decide that the default size for this image is too small, so you go to, or include in a page, the url /img/600x1000/Blog/22/test-what-happens-here.jpg. Tada, you have your image as 600px x 1000px.
  • For uses to be able to see/download the original file you can go to, or include in a page, the url /files/Blog/22/test-what-happens-here.jpg. Tada, you have your image as originally uploaded, 3000px x 5000px.

For none-image files the logic is slightly different:

  • As an admin user you go to the url /admin/attachments/add/Blog/22 and upload a file named "music.mp3" and give it a description of "great tune this one".
  • In this case it isn't an image, and if you include a link, or go to an 'image' url like /img/50x50/Blog/22/music-great-tune-this-one.mp3 you will get..... redirected to the url /img/types/mp3.png if the file exists or /img/types/generic.png if not
  • To access the original file you include a link, or go to the url /files/Blog/22/music-great-tune-this-one.mp3

And also...

A few other things are there in the code ;) but I'll leave describing them for another opportunity.

Wrapping Up

The source files described here allow you to upload files, restricted by extension, mimetype or size and associate them with any model in your application. Meta data is stored in the database while the files are stored out of the webroot. Files can be dynamically served/copied to the webroot upon demand.

Bake on!