ClientValidation for files (modern browsers)

There are cases that you want to validate the file format, size, extension etc but how to do that without submit the form ?

Also if the file is too large you have to wait for a cetain time until the error displayed back to you (bad to wait for a failed message)

An easy way to do that before submit the file is using javascript. But there is no way (not yet) to do this using clientValidate because the file validator does not provides javascript validator

So you have to write custom javascript validator.

Suppose we have a server side validator

In your model/yourModelform.php you should have something like that

(model file)

public function rules() {
        return array(
....
 array('file', 'file', 'types' => 'jpg', 'minSize'=>100, 'maxSize'=>1000000 , 'allowEmpty'=>false),
)),

Now in your form (that exist in your view/.../_form.php) that is relative with controller/update or controller/create you should to have this one

...
    echo $form->labelEx($model, 'file');
    echo $form->fileField($model, 'file');
    echo $form->error($model, 'file')
    ...

Modify the last line like that

echo $form->error($model, 'file', array('clientValidation' => 'js:customValidateFile(messages)'), false, true);

add this

$infoFieldFile = (end($form->attributes));
    //or $infoFieldFileID = CHtml::activeId($model, 'file');

right below of the $form->error($model, 'file'....

at the end of the _form.php file add this script

array('file', 'file', 'types' => 'jpg', 'minSize'=>100, 'maxSize'=>1000000 , 'allowEmpty'=>false),

<script>
    function customValidateFile(messages){
        var nameC= '#<?php echo $infoFieldFile['inputID']; ?>';
        //or var nameC= '#<?php echo $infoFieldFileID; ?>';

        var control = $(nameC).get(0);

        //simulates the required validator and allowEmpty setting of the file validator
        if (control.files.length==0) {
            messages.push("File is required");
            return false;
        }

        file = control.files[0];
        
        //simulates the types setting of the file validator
        if (file.name.substr((file.name.lastIndexOf('.') +1)) !='jpg') {
            messages.push("This is not a csv file");
            return false;
        }

       //simulates the maxSize setting of the file validator
        if (file.size>1000000) {
            messages.push("File cannot be too large (size cannot exceed 1.000.000 bytes.)");
            return false;
        }

       //simulates the minSize setting of the file validator
        if (file.size<100) {
            messages.push("File cannot be too small (size cannot be smaller 100 bytes.)");
            return false;
        }

       //simulates the format type (extra checking) see also http://en.wikipedia.org/wiki/Internet_media_type

        if (file.type!="image/jpeg") {
            messages.push("This is not a valid file");
            return false;
        }

    }

</script>

Also remember that is better to use this settings for your submit form

$form = $this->beginWidget(
                    'CActiveForm',
                    array(
                        'id' => 'your-id-form',
                        'enableClientValidation' => true,
                        'clientOptions' => array(
                            'validateOnSubmit' => true,
                        ),
                        'htmlOptions' => array('enctype' => 'multipart/form-data'),
                    )
    );

That's it!

Warning:

  1. The one that I described above, works only for modern browsers that support html5
  2. No one client validators can be replace server validator (for security reasons), so use both of them!