Thursday, August 23, 2007

PHP large file uploads

There are several upload limits you need to worry about in PHP. They are configurable in the php.ini file. The post_max_size is devious, as if a user uploads an image larger than this size, php will transfer no POST data to your script. If you look in the php log (generally set to /var/log/messages) you will see a line like this:

POST Content-Length of 66605360 bytes exceeds the limit of 8388608 bytes in Unknown on line 0

http://p2p.wrox.com/topic.asp?TOPIC_ID=23740 describes how to signal an error to the user, in this case.

There is an added complication if you use cake. Normally in cake, the same controller/action is used for both rendering the form and on form submit. The action identifies whether it should render the form or act on the submit based on the form data. If the form data is available it processes it, else it will pass back the form.

Since the only way we can figure out that the upload didn't work is by checking if the POST data is empty, we can't use this design for our controller/action in cake. What we could do is use different actions for rendering the form and submitting it. In the action for submit, we can check $this->data and alert the user if that is empty.

Here are the 2 example actions (file : app/controllers/profiles_controller.php)

// This function was written as a work-around for detecting failures in large POST
// requests - if the POST data is bigger than the php ini setting 'post_max_size',
// php will not transfer any POST data to the controller action, but if we use the same
// controller action for rendering the form as well as on form submit, we can't
// differentiate between the two.
function basics() {
if (!$this->data) {
$this->Session->setFlash("Couldn't upload the image, please choose a smaller image");
}
else {
$this->basic();
}
$this->redirect(array('controller'=>'profiles', 'action'=>'basic'), null, true);
}

function basic() {

// regular logic follows ...

}


Make sure the form app/views/profiles/basic.ctp directs the action to /profile/basics) :

create('User', array('type' => 'post', 'enctype' => 'multipart/form-data', 'url'=>'/profiles/basics')); ?>

No comments: