Search code examples
phpimagevalidationyiiyii2

Yii2 validate image post as data url


I need to validate image uploaded as data url which is in form:

{profile: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAA...McAAAAASUVORK5CYII="}

my Profile model has following rules:

    return [
        ...
        ['profile', 'image', 'extensions' => 'png, jpg',
            'minWidth' => 50, 'maxWidth' => 150,
            'minHeight' => 50, 'maxHeight' => 150,
        ],
        ...
     ];

my controller is saving the profile without validation:

        $my=new Profile();
        $my->profile=Yii::$app->request->post("profile");
        $result=$my->save(); //Always true even when dimensions are out of bound.

Problem: The image validation is not working for post data with image as string (data url).

Please help validate the image.

Update: Other question mentioned as duplicate is not related to Yii2 framework. I am looking for answer which will follow the MVC flow of Yii2.


Solution

  • I am answering my own question since I am not fully satisfied with any other answers found in similar questions, so that future readers may get benifits.

    The solution is to use custom validator.

    I have created one following the above guide:

    In my Profile model, I changed my rule as:

    return [
        ...
        ['profile', 'imageString', 'params' => ['mimes'=>['image/png', 'image/jpeg'],
            'minWidth' => 50, 'maxWidth' => 150,
            'minHeight' => 50, 'maxHeight' => 150,]
        ]
        ...
     ];
    

    and added a function:

    public function imageString($attr, $params){
        $img=explode(";",$this->$attr,2); //explodes into two part each containing image type and data
        if(count($img)<2){
            //expects two parts, else adds error.
            $this->addError($attr, 'Invalid imageString format.');
            return;
        }
        if(stripos($img[0],"data:")!==0 && stripos($img[1],"base64,")!==0){
            //expects "data:" and "base64," as starting strings for each parts (not case sensitive).
            $this->addError($attr, 'Invalid imageString format.');
            return;
        }
        $reps=[0,0];
        //removes the occurance of above starting strings (not case sensitive).
        $img[0]=str_ireplace("data:","",$img[0],$reps[0]);
        $img[1]=str_ireplace("base64,","",$img[1],$reps[1]);
        if(array_sum($reps)>2){
            //expects total occurances to be exact 2.
            $this->addError($attr, 'Invalid imageString format.');
            return;
        }
        $img[1]=getimagesizefromstring(base64_decode($img[1]));
        if(!$img[1]){
            //expects data to be valid image.
            $this->addError($attr, 'Invalid imageString format.');
            return;
        }
        foreach($params as $key => $val){
            switch($key){
                case "mimes": //check mime type of image.
                    $val = array_map('strtolower', $val);
                    if(!in_array(strtolower($img[0]),$val) || !in_array($img[1]['mime'],$val)){
                        $this->addError($attr, 'Invalid imageString mime type.');
                    }
                    break;
                case "minWidth": //check minimum width of image.
                    if($img[1][0]<$val){
                        $this->addError($attr, "Image width is lower than $val.");
                    }
                    break;
                case "maxWidth": //check maximum width of image.
                    if($img[1][0]>$val){
                        $this->addError($attr, "Image width is greater than $val.");
                    }
                    break;              
                case "minHeight": //check minimum height of image.
                    if($img[1][1]<$val){
                        $this->addError($attr, "Image height is lower than $val.");
                    }
                    break;
                case "maxHeight": //check maximum height of image.
                    if($img[1][1]>$val){
                        $this->addError($attr, "Image height is greater than $val.");
                    }
                    break;
            }
        }
    }
    

    This function uses getimagesizefromstring() for retrieving the image information from string.