Using the File Functions in CakePHP
In CakePHP, a very useful set of functions is included but which get little documentation. I had combed the web looking for something useful about how to use the Cake File functions but got little concise or tutorial-based help. After working with the File functions for a little bit now, I figured an explanation might be useful.
Add.ctp View File
Let’s do a simple file upload. Let’s assume that I’m creating blog posts (sorry if using blogs as examples is overkill, but overall I think everyone gets it). For each blog post entry I’d like to upload an image file to match it. In the views/posts/add.ctp file, I have the following basic code:
<h1>Add Post</h1>
<?=$form->create('Post',array('type'=>'file'));?>
<?=$form->input('date',array('label'=>'Publication Date '));?>
<?=$form->input('headline');?>
<?=$form->input('content');?>
<?=$form->input('image',array('type'=>'file'));?>
<?=$form->end('Submit');?>
In the Controller
Now, the most basic upload using the File functions is going to occur in the controllers/posts_controller.php file.
Everything should run like a normal Add function, except I need to actually do the upload of the file.
if ($this->data['Post']['image']) {
$file = new File($this->data['Post']['image']);
$ext = $file->ext();
if ($ext != 'jpg' && $ext != 'jpeg' && $ext != 'gif' && $ext != 'png') {
$this->Session->setFlash('You may only upload image files.');
$this->render();
} else {
$date = $this->data['Post']['date'];
$filename = $date['year'].'-'.$date['month'].'-'.$date['day'].'-post-image.'.$ext;
$data = $file->read();
$file->close();
$file = new File(WWW_ROOT.'/img/'.$filename,true);
$file->write($data);
$file->close();
}
}
Let me explain what is happening here. When the user chooses a file to be uploaded in the HTML form, the link to that file is set through the $_POST variables, and in Cake, as will all form posts, these get parsed into the $this->data array.
The first step, then, is to create a new instance of the File class so we can use those functions.
$file = new File($this->data['Post']['image']);
From here on out, I can pull all sorts of information about this file by using the available Cake functions. You’ll notice that I pulled out the file extension quite easily by using the File->ext() function:
$ext = $file->ext();
If I were to use an echo command in PHP, I would get
jpg
for a JPG file upload.
To write the contents of this file to the server, I need to first fetch all the data in the file.
$data = $file->read();
Now all the contents of the file are held in the $data variable. To write this to the server, I’ll need to create a new file instance, create the file in the appropriate directory (in this case, the webroot/img folder), and dump the contents of $data into that file.
$file->close(); //close out of the user's file
$file = new File(WWW_ROOT.'/img'.$filename,true);
Notice that in creating the new instance for the File class, I’ve used the Cake global variable, WWW_ROOT and I’ve appended the /img directory plus the desired filename. Also, I’ve set create new file to true.
Now the $file variable is set to the new object, which in this case is the empty file on my server stored in the webroot/img directory named whatever is in $filename. By using the write() function, I can plop $data into that file:
$file->write($data);
The rest of the script is just closing out everything.
Other Functions
So how about it? Much, much easier than doing all the classic fopen and fread, fputs functions in PHP. Of course you can branch out and do more as well as error checking using other parameters in the functions themselves.
The CakePHP API contains a list of the available File and Folder functions. By using them like you’ve seen me do above, you can spare yourself superfluous coding.
For example, if I wanted to test if the file by that filename already exists on the server, I can enter:
$id = intval(rand());
$filename = $id.'-post.'.$file->ext();
$folder = new Folder(WWW_ROOT.'/img');
while ($folder->find($filename) == $filename) {
$id++;
$filename = $id.'-post.'.$file->ext();
}
Or if I wanted to delete a file on the server where $file has been set as the class object of the file to be deleted:
$file->delete();
So explore the other File functions and enjoy!
Comments
links for 2008-02-02 « Richard@Home
Feb 1st, 2008, 11:17 pm
[...] David Golding Design Blog - Using the File Functions in CakePHP (tags: cakephp file upload) [...]
Feb 2nd, 2008, 9:37 pm
Daniel,
Thanks for the tip. The article now reflects the change.
Mar 24th, 2008, 12:50 am
Hi David, well first of all, let me tell you that this post was a great help for me, I was looking for something similar using the cakephp framework ( 1.2), and well, I just follow the steps in this post, but I had some issues trying with this way, I just modified some parts in order to accommodate to my web application.
-->ISSUES:
1.- this sentence:
$file = new File($this->data['Post']['image']);
My browser crash when I try to use it.
It will be :
$file = new File($this->data['Post']['image']['tmp_name']);
2.-
Seems like the ext function in the API of cakephp(1.2) is not working correctly.
I was gotten this:
Array ( [Imagenes] => Array ( [archivo] => Array ( [name] => wink.gif [type] => image/gif [tmp_name] => C:\WINDOWS\TEMP\php12.tmp [error] => 0 [size] => 4164 ) ) )
using the ext() function it's return = tmp
without ext()function it's return = gif
So I did the next in order to fix it:
-- My view add.ctp:
Nueva Imagen
create('Imagen',array('url' => '/imagenes/add','type'=>'file'));
echo $form->input('nombre',array('type'=>'file'));
echo $form->end('Submit');
?>
-- My controller(imagenes_controller.php):
function add()
{
if ($this->data['Imagen']['nombre'])
{
$file_1 = new File($this->data['Imagen']['nombre']['tmp_name']);
/* Calculating the extension of the file */
#First getting the file with the extension
$ext = $this->data['Imagen']['nombre']['name'];
#the separator.
$punto = strrpos($ext,".");
#getting the extension.
$ext = substr($ext,$punto+1,(strlen($ext)-$punto));
if ($ext = 'jpeg' && $ext
= 'png')
{
$this->Session->setFlash('You may only upload image files.');
$this->render();
}
else
{
$filename = 'img_'.time().'.'.$ext;
$data = $file_1->read();
$file_1->close();
$file_2 = new File(WWW_ROOT.'/files/'.$filename,true);
$file_2->write($data);
$file_2->close();
}
}
}
and that's all, just to let you know, because I spent a lot time trying that this worked on my web application. :D
Regards,
Lucks
Mar 27th, 2008, 3:49 pm
Lucks,
Gracias por escribir sus pensamientos en cuanto a mis instrucciones sobre el uso de Cake. Por mi parte, la transferencia de archivos por Cake es una de las cosas mas dificiles de la programación. Creo que las problemas que descubrió de mi artÃculo fueron cosas del tratamiento de Cake con la internacionalización (i10n). VeÃa que los variables que usó tenÃan nombres en español, entonces, es posible que los funciones no seguÃan correctamente. Pero no he tenido mucha oportunidad para experimentar sobre los funciones de internacionalización de Cake. Pues, buena suerte con Cake especialmente con este función de la transferencia de archivos.
Thanks for writing your thoughts about my instructions on how to use Cake. Personally, I think that uploading files with Cake is one of the more difficult parts of web programming. I think the problems you found in my post were things relating to how Cake manages internationalization (i10). I noticed that the variables you used had Spanish names, so it's possible that the upload functions didn't behave properly. But I haven't had much of an opportunity to experiment with Cake's internationalization functions. Anyhow, good luck with Cake, especially with this upload function.
mjavier2k
Apr 16th, 2008, 2:45 am
Hi, thanks for the post.

Daniel Hofstetter
Feb 1st, 2008, 4:13 am
When creating the form you also have to define the type as "file":
$form->create('Post', array('type' => 'file'));
This adds enctype="multipart/form-data" to the generated html form definition.