Need Help With File Upload Client Validation

Hi,

I need to enable file upload validation on clientside (to check file extension, maxsize, etc)

I tried setting ‘enableClientValidation’ => true, but no effect.

Here’s my code snippet

model rules:




array('uploadedfile1', 'file', 

	'allowEmpty'=>false,

	'enableClientValidation' => true,

	'types'=>'jpg,jpeg,gif,png',

	'maxSize' => 1024 * 1024 * 1, // 1MB

), 



in the view file:




<?php $form=$this->beginWidget('bootstrap.widgets.TbActiveForm',array(

	'id'=>'users-form',

	'type'=>'horizontal',

	'enableClientValidation'=>true,

	'enableAjaxValidation'=>false,

	'clientOptions' => array(

	'validateOnSubmit'=>true,

	'validateOnType'=>false,

	'validateOnChange'=>false,

	)

)); 


....


<?php echo $form->fileFieldRow($model, 'uploadedfile1'); ?>


....


?>



Any Help? Thanks

fyi: there is no uploadedfile1 field in the database, it’s just a variable in the class

i think i’m having that same issue…

but i’ve never done file uploading with yii so I might be doing something wrong

will post here if I learn something…

EDIT

don’t know whether it will help you but,




    'stateful' => true,

    'htmlOptions' => array('enctype' => 'multipart/form-data'),

is recommended as options for your activeform

The problem is a little bit deeper than what you think,

Unfortunately clientValidateAttribute has not been implemented on CFileValidator, so the only solution is to implement the method all by yourself and it’s like a piece of cake on the top of mount Everest

Good luck writing js ;)

ok, i have just wrote the method to work on client side, here is the code

you must copy the following code into the CFileValidator class at framework/validators/CFileValidator.php

it checks the file extension and emptiness and has been tested on IE7, Firefox, Chrome and opera (i hope it saves a few hours of your precious time ;) )


    

   /**

     * Returns the JavaScript needed for performing client-side validation.

     * @param CModel $object the data object being validated

     * @param string $attribute the name of the attribute to be validated.

     * @return string the client-side validation script.

     * @see CActiveForm::enableClientValidation

     * @since 1.1.7

     */

    public function clientValidateAttribute($object, $attribute)

    {

        $js = '';


        if (!$this->allowEmpty) {


            $message = $this->message;

            if ($message == null)

                $message = Yii::t('yii', '{attribute} cannot be blank.');


            $message = strtr($message, array(

                '{attribute}' => $object->getAttributeLabel($attribute),

            ));

            $js .= '

            if($.trim(value)==""){messages.push(' . CJSON::encode($message) . ');}

            ';

        }


        if ($this->types !== null) {

            if (is_string($this->types))

                $types = preg_split('/[\s,]+/', strtolower($this->types), -1, PREG_SPLIT_NO_EMPTY);

            else

                $types = $this->types;


            $message = $this->message;

            if ($message == null)

                $message = Yii::t('yii', 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.');

            $message = strtr($message, array(

                '{file}' => ':file',

                '{extensions}' => implode(', ', $types),

            ));


            $js .= "

            if(['" . implode("','", $types) . "'].indexOf($.trim(value).split('.').pop().toLowerCase()) == -1)

            {

                messages.push('" . $message . "'.replace(':file', $.trim(value)));

            }

            ";

        }

        return $js;

    }

Hello All,

Thank you very much for your contributions. This has helped us solved the problem to validate the file rules without having to actually send it to the server.

Shahdoost, thanks for your code. We did some additional things that helped us and we want to share back.

[list=1]

[*]We created an Extension class that extends from CFileValidator so that we did not need to hack the framework

[*]We changed the message for the types to $this->wrongType to get the appropriate message from our rules

[*]We added another method to validate the file size. Note: This may only work on certain browsers (In other words…not in explorer)

[/list]




class EFileValidator extends CFileValidator {


    /**

     * Returns the JavaScript needed for performing client-side validation.

     * @param CModel $object the data object being validated

     * @param string $attribute the name of the attribute to be validated.

     * @return string the client-side validation script.

     * @see CActiveForm::enableClientValidation

     * @since 1.1.7

     */

    public function clientValidateAttribute($object, $attribute) {

        $js = '';


        if (!$this->allowEmpty) {


            $message = $this->message;

            if ($message == null)

                $message = Yii::t('yii', '{attribute} cannot be blank.');


            $message = strtr($message, array(

                '{attribute}' => $object->getAttributeLabel($attribute),

                    ));

            $js .= '

            if($.trim(value)==""){messages.push(' . CJSON::encode($message) . ');}

            ';

        }


        if ($this->types !== null) {

            if (is_string($this->types))

                $types = preg_split('/[\s,]+/', strtolower($this->types), -1, PREG_SPLIT_NO_EMPTY);

            else

                $types = $this->types;


            $message = $this->wrongType;

            if ($message == null)

                $message = Yii::t('yii', 'The file "{file}" cannot be uploaded. Only files with these extensions are allowed: {extensions}.');

            $message = strtr($message, array(

                '{file}' => ':file',

                '{extensions}' => implode(', ', $types),

                    ));


            $js .= "

            if(['" . implode("','", $types) . "'].indexOf($.trim(value).split('.').pop().toLowerCase()) == -1)

            {

                messages.push('" . $message . "'.replace(':file', $.trim(value)));

            }

            ";

        }


        /**

         * Check the maxfile size setting

         */

        if ($this->maxSize !== null) {

            $message = $this->tooLarge;

            

            $inputId = get_class($object)."_".$attribute;

            

            $js .= "

                var fileSize = $('#$inputId')[0].files[0].size; 

                if(fileSize>$this->maxSize){

                    messages.push('" . $message . "');

                }

                ";

        }

        return $js;

    }


}



After this was done, we did our rules and worked like a charm.




array('photoFile','ext.fileValidatorOnSteroids.validators.EFileValidator','types'=>'jpg,gif,png,jpeg','maxSize'=>5242880,

                "tooLarge"=>"Please choose a file with size up to 5MB",

                "wrongType"=>"Your photo must be a jpg,gif or png.",

                "enableClientValidation"=>true

                )



Hi, I try it, seems don’t work :(

I forgot to write in the form :


echo $form->error($model, attributeName);

many thanks :)

Hi, I’ve in my view (CActiveForm) 3 fields :


<input type="file"...>   <!- binaryfile -->

<input type="password"...>

<input type="repeat_password"...>

my model rules here :


public function rules()

    {

        // NOTE: you should only define rules for those attributes that

        // will receive user inputs.

        return array(

            array('binaryfile','ext.validators.EFileValidator',

                'types'=>'jpg,gif,png,jpeg',

                'maxSize'=>5242880,

                "tooLarge"=>"Please choose a file with size up to 5MB",

                "wrongType"=>"Your photo must be a jpg,gif or png .",

                "allowEmpty" => true,

                "enableClientValidation"=>true

                ),


            array('new_password, new_password_repeat','required', 'on'=>'create',"enableClientValidation"=>true ),

            array('new_password','match', 'pattern'=>'#^[0-9A-Za-z]+$#', 'message'=>'Only the following characters are allowed: 0-9 A-Z a-z',"enableClientValidation"=>true ),

            array('new_password','length','max'=>25,"enableClientValidation"=>true),   // PW only 20 Chars

            array('new_password','length','min'=>6,"enableClientValidation"=>true),    // PW min 6 Chars

            array('new_password','compare', 'compareAttribute'=>'new_password_repeat',"enableClientValidation"=>true),//  -> PASSWORD REPEAT

            array('new_password, new_password_repeat','safe'),        //  -> PASSWORD REPEA

           

            array('personale_id', 'numerical', 'integerOnly'=>true),

            

            array('id, fileName, fileType, fileSize', 'safe'),

            array('id, personale_id, fileName, fileType, fileSize', 'safe', 'on'=>'search'),            

        );

    }

if I select a file the ClientValidation works for all three… if ignore it and put a password, the form is submitted … :blink:

Nice one it’s help me but on




              $js .= "

                var fileSize = $('#$inputId')[0].files[0].size; 

                if(fileSize>$this->maxSize){

                    messages.push('" . $message . "');

                }

                ";



It must be




              $js .= "

                if ($('#$inputId')[0].files[0]){

                    var fileSize = $('#$inputId')[0].files[0].size; 

                    if(fileSize>$this->maxSize){

                        messages.push('" . $message . "');

                    }

                }

                ";



If the user not upload the file the js not work.

Regards,

Sami.

Awesome!! I was looking for this only…

Just one little bug…when allowEmpty is false and no file is uploaded in form, its gives "file type" error.

So change this


if(['" . implode("','", $types) . "'].indexOf($.trim(value).split('.').pop().toLowerCase()) == -1)

to


if(['" . implode("','", $types) . "'].indexOf($.trim(value).split('.').pop().toLowerCase()) == -1".($this->allowEmpty?" && $.trim(value)!=''":'').")

Another minor problem:




            $message = $this->tooLarge;



should be:




            $message=$this->tooLarge!==null?$this->tooLarge : Yii::t('yii','The file is too large. Its size cannot exceed {limit} bytes.');

            $message = strtr($message, array(

                '{limit}' => $this->getSizeLimit()

                ));



Also don’t forget to set enableClientValidation on your form - like I did!

Note that even with this there is still a problem with validation wrt the file size, because the user can still hit Submit and the input will fail silently. See this post for more info