MeioUpload Behavior

This article is also available in the following languages:
By vbmendes
A behavior to make the file uploads as simple as defining variables.
After about one year trying to make the upload simple in CakePHP, I finally finished an upload behavior based on digital spaghetti's (http://digitalspaghetti.tooum.net/) upload behavior (http://digitalspaghetti.tooum.net/switchboard/blog/2497:Upload_Behavior_for_CakePHP_12) and I am glad to show it for you.

You just have to set the MeioUpload Behavior:

Model Class:

<?php 
class MyModel extends AppModel {
    var 
$actsAs = array('MeioUpload' => array(
        
'filename' => array(
            
'dir' => 'files/images',
        )
    );        
}
?>

And add the file input in the form:

View Template:


<?php echo $form->create('Produto',array('type' => 'file'));?>
<?php 
echo $form->input('filename', array('type' => 'file')); ?>

Here is the link to the snippet with the behavior code:
http://cakeforge.org/snippet/detail.php?type=snippet&id=226
And here id the link to the documentation:
http://www.meiocodigo.com/projects/meioupload

Comments

  • Posted 04/13/11 06:04:12 PM
    Hello,

    I modified MeioUpload Behavior for use it in several model. THis is de code:


     array(
     *       'filename' => array(
     *           'dir' => 'files/images',
     *           'create_directory' => false,
     *           'allowed_mime' => array('image/jpeg', 'image/pjpeg', 'image/gif', 'image/png'),
     *           'allowed_ext' => array('.jpg', '.jpeg', '.png', '.gif'),
     *           'thumbsizes' => array(
     *                  'small'  => array('width'=>100, 'height'=>100),
     *                  'medium' => array('width'=>220, 'height'=>220),
     *                  'large'  => array('width'=>800, 'height'=>600)
     *           )
     *       )
     *     )
     * );
     * The above code will save the uploaded file's name in the 'filename' field in database,
     * it will not overwrite existing files, instead it will create a new filename based on the original
     * plus a counter.
     * Allowed Mimetypes and extentions should be pretty explanitory.
     * For thumbnails, when the file is uploaded, it will create 3 thumbnail sizes and prepend the name
     * to the thumbfiles (i.e. image_001.jpg will produced thumb.small.image_001.jpg, thumb.medium.image_001.jpg, etc)
     *
     * 5) Create your upload view, make sure it's a multipart/form-data form, and the filename field is of type $form->file
     * 6) Make sure your directory is at least CHMOD 775, also check your php.ini MAX_FILE_SIZE is enough to support the filesizes you are uploading
     *
     * Version Details
     *
     * 1.0.1
     * + Fixed a bug in the create folder method
     * + Now you can use the $validate var of the model to apply the changes to default validation rules;
     * + Changed the my_array_merge function, now it's part of the behavior, name arrayMerge;
     * + Allow use of {DS}, {model} and {field} constants in directory name and fields names;
     * + Fixed a bug with the replacement of the default names.
     *
     * 1.0
     * + Initial release.
     */

    uses('folder');

    class MeioUploadBehavior extends ModelBehavior {
        /**
         * The default options for the behavior
         */
        var $default_options = array(
            'dir' => '',
            'allowed_mime' => array(),
            'allowed_ext' => array(),
            'create_directory' => true,
            'max_size' => 2097152,
            'thumbsizes' => array(),
            'default' => false,
            'fields' => array(
                'dir' => 'dir',
                'filesize' => 'filesize',
                'mimetype' => 'mimetype'
            ),
            'validations' => array(
                'FieldName' => array(
                    'rule' => array('uploadCheckFieldName'),
                    'check' => true,
                    'message' => 'Este campo no se ha definido entre los parámetros de MeioUploadBehavior.'
                ),
                'Dir' => array(
                    'rule' => array('uploadCheckDir'),
                    'check' => true,
                    'message' => 'El directorio donde este archivo se coloca no existe o está protegido contra escritura.'
                ),
                'Empty' => array(
                    'rule' => array('uploadCheckEmpty'),
                    'check' => true,
                    'on' => 'create',
                    'message' => 'El archivo no puede estar vacío'
                ),
                'UploadError' => array(
                    'rule' => array('uploadCheckUploadError'),
                    'check' => true,
                    'message' => 'Hubo problemas al cargar el archivo.'
                ),
                'MaxSize' => array(
                    'rule' => array('uploadCheckMaxSize'),
                    'check' => true,
                    'message' => 'El tamaño máximo de archivo se ha superado.'
                ),
                'InvalidMime' => array(
                    'rule' => array('uploadCheckInvalidMime'),
                    'check' => true,
                    'message' => 'Tipo de archivo inválido.'
                ),
                'InvalidExt' => array(
                    'rule' => array('uploadCheckInvalidExt'),
                    'check' => true,
                    'message' => 'Extensión de archivo inválida.'
                )
            )
        );

        var $default_validations = array(
            'FieldName' => array(
                'rule' => array('uploadCheckFieldName'),
                'check' => true,
                'message' => ' campo no se ha definido entre los parámetros de MeioUploadBehavior.'
            ),
            'Dir' => array(
                'rule' => array('uploadCheckDir'),
                'check' => true,
                'message' => 'El directorio donde este archivo se coloca no existe o está protegido contra escritura.'
            ),
            'Empty' => array(
                'rule' => array('uploadCheckEmpty'),
                'check' => true,
                'on' => 'create',
                'message' => 'El archivo no puede estar vacío'
            ),
            'UploadError' => array(
                'rule' => array('uploadCheckUploadError'),
                'check' => true,
                'message' => 'Hubo problemas al cargar el archivo.'
            ),
            'MaxSize' => array(
                'rule' => array('uploadCheckMaxSize'),
                'check' => true,
                'message' => 'El tamaño máximo de archivo se ha superado.'
            ),
            'InvalidMime' => array(
                'rule' => array('uploadCheckInvalidMime'),
                'check' => true,
                'message' => 'Tipo de archivo inválido.'
            ),
            'InvalidExt' => array(
                'rule' => array('uploadCheckInvalidExt'),
                'check' => true,
                'message' => 'Extensión de archivo inválida.'
            )
        );

        /**
         * The message for move error.
         */
        var $moveErrorMsg = 'Hubo problemas en la copia de archivo.';

        /**
         * The array that saves the $options for the behavior
         */
        var $__fields = array();

        /**
         * Patterns of reserved words
         */
        var $patterns = array(
            "thumb",
            "default"
        );

        /**
         * Words to replace the patterns of reserved words
         */
        var $replacements = array(
            "t_umb",
            "d_fault"
        );

        /**
         * Array of files to be removed on the afterSave callback
         */
        var $__filesToRemove = array();

        /**
         * Setup the behavior. It stores a reference to the model, merges the default options with the options for each field, and setup the validation rules.
         *
         * @author Vinicius Mendes
         * @return null
         * @param $model Object
         * @param $config Array[optional]
         */
        function setup(&$model, $config=array()) {
            $this->Folder[$model->name] = &new Folder;
            $this->__model[$model->name] = $model;
            $this->__fields[$model->name] = array();
            foreach($config as $field => $options) {

                // Check if given field exists
                if(!$model->hasField($field)) {
                    trigger_error('MeioUploadBehavior Error: The field "'.$field.'" doesn\'t exists in the model "'.$model->name.'".', E_USER_WARNING);
                }

                // Merge given options with defaults
                $options = $this->arrayMerge($this->default_options, $options);
                // Including the default name to the replacements
                if($options['default']){
                    if(!preg_match('/^.+\..+$/',$options['default'])){
                        trigger_error('MeioUploadBehavior Error: The default option must be the filename with extension.', E_USER_ERROR);
                    }
                    $this->_includeDefaultReplacement($options['default']);
                }
                // Verifies if the thumbsizes names is alphanumeric
                foreach($options['thumbsizes'] as $name => $size){
                    if(!preg_match('/^[0-9a-zA-Z]+$/',$name)){
                        trigger_error('MeioUploadBehavior Error: The thumbsizes names must be alphanumeric.', E_USER_ERROR);
                    }
                }
                // Process the max_size if it is not numeric
                $options['max_size'] = $this->sizeToBytes($options['max_size']);
                $this->__fields[$model->name][$field] = $options;


                // Generate temporary directory if none provided
                if(empty($options['dir'])) {
                    $this->__fields[$model->name][$field]['dir'] = 'uploads' . DS . $model->name;
                // Else replace the tokens of the dir.
                } else {
                    $this->__fields[$model->name][$field]['dir'] = $this->replaceTokens($model, $options['dir'],$field);
                }

                // Replace tokens in the fields names.
                foreach($this->__fields[$model->name][$field]['fields'] as $fieldToken => $fieldName){
                    $this->__fields[$model->name][$field]['fields'][$fieldToken] = $this->replaceTokens($model, $fieldName,$field);
                }

                // Check that the given directory does not have a DS on the end
                if($options['dir'][strlen($options['dir'])-1] == DS) {
                    $options['dir'] = substr($options['dir'],0,strlen($options['dir'])-2);
                }
            }
        }

        /**
         * Merges two arrays recursively
         *
         * @author Vinicius Mendes
         * @return Array
         * @param $arr Array
         * @param $ins Array
         */
        function arrayMerge($arr, $ins) {
            if (is_array($arr)) {
                if (is_array($ins)) {
                    foreach ($ins as $k=>$v) {
                        if (isset($arr[$k])&&is_array($v)&&is_array($arr[$k])) {
                            $arr[$k] = $this->arrayMerge($arr[$k],$v);
                        }
                        else $arr[$k] = $v;
                    }
                }
            } elseif (!is_array($arr)&&(strlen($arr)==0||$arr==0)) {
                $arr=$ins;
            }
            return($arr);
        }

        /**
         * Replaces some tokens. {model} to the underscore version of the model name, {field} to the field name, {DS}. / or \ to DS constant value.
         *
         * @author Vinicius Mendes
         * @return String
         * @param $string String
         * @param $fieldName String
         */
        function replaceTokens($model, $string,$fieldName){
            return str_replace(array('{model}', '{field}', '{DS}','/','\\'),array(Inflector::underscore($this->__model[$model->name]->name),$fieldName,DS,DS,DS),$string);
        }

        /**
         * Convert a size value to bytes. For example: 2 MB to 2097152.
         *
         * @author Vinicius Mendes
         * @return int
         * @param $size String
         */
        function sizeToBytes($size){
            if(is_numeric($size)) return $size;
            if(!preg_match('/^[1-9][0-9]* (kb|mb|gb|tb)$/i', $size)){
                trigger_error('MeioUploadBehavior Error: The max_size option format is invalid.', E_USER_ERROR);
                return 0;
            }
            list($size, $unit) = explode(' ',$size);
            if(strtolower($unit) == 'kb') return $size*1024;
            if(strtolower($unit) == 'mb') return $size*1048576;
            if(strtolower($unit) == 'gb') return $size*1073741824;
            if(strtolower($unit) == 'tb') return $size*1099511627776;
            trigger_error('MeioUploadBehavior Error: The max_size unit is invalid.', E_USER_ERROR);
            return 0;
        }

        /**
         * Sets the validation for each field, based on the options.
         *
         * @author Vinicius Mendes
         * @return null
         * @param $fieldName String
         * @param $options Array
         */
        function setupValidation($model, $fieldName, $options){
            $options = $this->__fields[$model->name][$fieldName];

            if(isset($this->__model[$model->name]->validate[$fieldName])){
                if(isset($this->__model[$model->name]->validate[$fieldName]['rule'])){
                    $this->__model[$model->name]->validate[$fieldName] = array(
                        'oldValidation' => $this->__model[$model->name]->validates[$fieldName]
                    );
                }
            } else {
                $this->__model[$model->name]->validate[$fieldName] = array();
            }
            $this->__model[$model->name]->validate[$fieldName] = $this->arrayMerge($this->default_validations,$this->__model[$model->name]->validate[$fieldName]);
            $this->__model[$model->name]->validate[$fieldName] = $this->arrayMerge($options['validations'],$this->__model[$model->name]->validate[$fieldName]);
        }

        /**
         * Checks if the field was declared in the MeioUpload Behavior setup
         *
         * @author Vinicius Mendes
         * @return boolean
         * @param $model Object
         * @param $data Array
         */
        function uploadCheckFieldName(&$model, $data,$other){
            foreach($data as $fieldName => $field){
                if(!$this->__model[$model->name]->validate[$fieldName]['FieldName']['check']) return true;
                if(isset($this->__fields[$model->name][$fieldName])){
                    return true;
                } else {
                    $this->log('UploadBehavior Error: The field "'.$fieldName.'" wasn\'t declared as part of the UploadBehavior in model "'.$model->name.'".');
                    return false;
                }
            }
            return true;
        }

        /**
         * Checks if the folder exists or can be created or writable.
         *
         * @author Vinicius Mendes
         * @return boolean
         * @param $model Object
         * @param $data Array
         */
        function uploadCheckDir(&$model, $data){
            foreach($data as $fieldName => $field){
                if(!$this->__model[$model->name]->validate[$fieldName]['Dir']['check']) return true;
                $options = $this->__fields[$model->name][$fieldName];
                if(empty($field['remove']) || empty($field['name'])){
                    // Check if directory exists and create it if required
                    if(!is_dir($options['dir'])) {
                        if($options['create_directory']){
                            if(!$this->Folder[$model->name]->create($options['dir'])) {
                                trigger_error('UploadBehavior Error: The directory '.$options['dir'].' does not exist and cannot be created.', E_USER_WARNING);
                                return false;
                            }
                        } else {
                            trigger_error('UploadBehavior Error: The directory'.$options['dir'].' does not exist.', E_USER_WARNING);
                            return false;
                        }
                    }

                    // Check if directory is writable
                    if(!is_writable($options['dir'])) {
                        trigger_error('UploadBehavior Error: The directory '.$options['dir'].' isn\'t writable.', E_USER_WARNING);
                        return false;
                    }
                }
            }
            return true;
        }

        /**
         * Checks if the filename is not empty.
         *
         * @author Vinicius Mendes
         * @return boolean
         * @param $model Object
         * @param $data Array
         */
        function uploadCheckEmpty(&$model, $data){
            foreach($data as $fieldName => $field){
                if(!$this->__model[$model->name]->validate[$fieldName]['Empty']['check']) return true;
                if(empty($field['remove'])){
                    if(!is_array($field) || empty($field['name'])){
                        return false;
                    }
                }
            }
            return true;
        }

        /**
         * Checks if ocurred erros in the upload.
         *
         * @author Vinicius Mendes
         * @return boolean
         * @param $model Object
         * @param $data Array
         */
        function uploadCheckUploadError(&$model, $data){
            foreach($data as $fieldName => $field){
                if(!$this->__model[$model->name]->validate[$fieldName]['UploadError']['check']) return true;
                if(!empty($field['name']) && $field['error'] > 0){
                    return false;
                }
            }
            return true;
        }

        /**
         * Checks if the file isn't bigger then the max file size option.
         *
         * @author Vinicius Mendes
         * @return boolean
         * @param $model Object
         * @param $data Array
         */
        function uploadCheckMaxSize(&$model, $data){
            foreach($data as $fieldName => $field){
                if(!$this->__model[$model->name]->validate[$fieldName]['MaxSize']['check']) return true;
                $options = $this->__fields[$model->name][$fieldName];
                if(!empty($field['name']) && $field['size'] > $options['max_size']) {
                    return false;
                }
            }
            return true;
        }

        /**
         * Checks if the file is of an allowed mime-type.
         *
         * @author Vinicius Mendes
         * @return boolean
         * @param $model Object
         * @param $data Array
         */
        function uploadCheckInvalidMime(&$model, $data){
            foreach($data as $fieldName => $field){
                if(!$this->__model[$model->name]->validate[$fieldName]['InvalidMime']['check']) return true;
                $options = $this->__fields[$model->name][$fieldName];
                if(!empty($field['name']) && count($options['allowed_mime']) > 0 && !in_array($field['type'], $options['allowed_mime'])) {
                    return false;
                }
            }
            return true;
        }

        /**
         * Checks if the file has an allowed extension.
         *
         * @author Vinicius Mendes
         * @return boolean
         * @param $model Object
         * @param $data Array
         */
        function uploadCheckInvalidExt(&$model, $data){
            foreach($data as $fieldName => $field){
                if(!$this->__model[$model->name]->validate[$fieldName]['InvalidExt']['check']) return true;
                $options = $this->__fields[$model->name][$fieldName];
                if(!empty($field['name'])){
                    if(count($options['allowed_ext']) > 0) {
                        $matches = 0;
                        foreach($options['allowed_ext'] as $extension) {
                            if(strtolower(substr($field['name'],-strlen($extension))) == strtolower($extension)) {
                                $matches++;
                            }
                        }

                        if($matches == 0) {
                            return false;
                        }
                    }
                }
            }
            return true;
        }

        /**
         * Set a file to be removed in afterSave callback
         *
         * @author Vinicius Mendes
         * @return null
         * @param $fieldName String
         */
        function setFileToRemove($model,$fieldName){
            $filename = $this->__model[$model->name]->field($fieldName);
            if(!empty($filename) && $filename != $this->__fields[$model->name][$fieldName]['default']){
                $this->__filesToRemove[$model->name][] = array(
                    'dir' => $this->__fields[$model->name][$fieldName]['dir'],
                    'name' => $filename
                );
            }
        }

        /**
         * Include a pattern of reserved word based on a filename, and it's replacement.
         *
         * @author Vinicius Mendes
         * @return null
         * @param $default String
         */
        function _includeDefaultReplacement($default){
            $replacements = $this->replacements;
            list($newPattern, $ext) = $this->splitFilenameAndExt($default);
            if(!in_array($newPattern, $this->patterns)){
                $this->patterns[] = $newPattern;
                $newReplacement = $newPattern;
                if(isset($newReplacement[1])){
                    if($newReplacement[1] != '_'){
                        $newReplacement[1] = '_';
                    } else {
                        $newReplacement[1] = 'a';
                    }
                } elseif($newReplacement != '_') {
                    $newReplacement = '_';
                } else {
                    $newReplacement = 'a';
                }
                $this->replacements[] = $newReplacement;
            }
        }

        /**
         * Removes the bad characters from the $filename and replace reserved words. It updates the $model->data.
         *
         * @author Vinicius Mendes
         * @return null
         * @param $fieldName String
         */
        function fixName($model, $fieldName){
            // updates the filename removing the keywords thumb and default name for the field.
            list($filename, $ext) = $this->splitFilenameAndExt($this->__model[$model->name]->data[$this->__model[$model->name]->name][$fieldName]['name']);
            $filename = str_replace($this->patterns,$this->replacements,$filename);
            $filename = Inflector::slug($filename);
            $i = 0;
            $newFilename = $filename;
            while(file_exists($this->__fields[$model->name][$fieldName]['dir'].DS.$newFilename.'.'.$ext)){
                $newFilename = $filename.$i;
                $i++;
            }
            $this->__model->data[$model->name][$this->__model[$model->name]->name][$fieldName]['name'] = $newFilename.'.'.$ext;
        }

        /**
         * Splits a filename in two parts: the name and the extension. Returns an array with it respectively.
         *
         * @author Vinicius Mendes
         * @return Array
         * @param $filename String
         */
        function splitFilenameAndExt($filename){
            $parts = explode('.',$filename);
            $ext = $parts[count($parts)-1];
            unset($parts[count($parts)-1]);
            $filename = implode('.',$parts);
            return array($filename,$ext);
        }

        /**
         * Sets the validation rules for each field.
         *
         * @return true
         * @param $model Object
         */
        function beforeValidate(&$model) {
            foreach($this->__fields[$model->name] as $fieldName=>$options){
                $this->setupValidation($model, $fieldName, $options);
            }
            return true;
        }

        /**
         * Uploads the files before saving the record.
         *
         * @author Vinicius Mendes
         * @param $model Object
         */
        function beforeSave(&$model) {
            foreach($this->__fields[$model->name] as $fieldName=>$options){
                // if the file is marked to be deleted, use the default or set the field to null
                if(!empty($model->data[$model->name][$fieldName]['remove'])){
                    if($options['default']){
                        $model->data[$model->name][$fieldName] = $options['default'];
                    } else {
                        $model->data[$model->name][$fieldName] = null;
                    }
                    //if the record is already saved in the database, set the existing file to be removed after the save is sucessfull
                    if(!empty($model->data[$model->name][$model->primaryKey])){
                        $this->setFileToRemove($model,$fieldName);
                    }
                    continue;
                }

                // If no file has been upload, then unset the field to avoid overwriting existant file
                if(!isset($model->data[$model->name][$fieldName]) || !is_array($model->data[$model->name][$fieldName]) || empty($model->data[$model->name][$fieldName]['name'])){
                    if(!empty($model->data[$model->name][$model->primaryKey]) || !$options['default']){
                        unset($model->data[$model->name][$fieldName]);
                    } else {
                        $model->data[$model->name][$fieldName] = $options['default'];
                    }
                    continue;
                }
                //if the record is already saved in the database, set the existing file to be removed after the save is sucessfull
                if(!empty($model->data[$model->name][$model->primaryKey])){
                    $this->setFileToRemove($model,$fieldName);
                }

                // Fix the filename, removing bad characters and avoiding from overwriting existing ones
                $this->_includeDefaultReplacement($options['default']);
                $this->fixName($model, $fieldName);
                $saveAs = $options['dir'].DS.$model->data[$model->name][$fieldName]['name'];

                // Attempt to move uploaded file
                if(!move_uploaded_file($model->data[$model->name][$fieldName]['tmp_name'], $saveAs)){
                    $model->validationErrors[$field] = $moveErrorMsg;
                    return false;
                }

                // It the file is an image, try to make the thumbnails
                if (count($options['allowed_ext']) > 0 && in_array($model->data[$model->name][$fieldName]['type'], array('image/jpeg', 'image/pjpeg', 'image/png'))) {
                    foreach ($options['thumbsizes'] as $key => $value) {
                        // If a 'normal' thumbnail is set, then it will overwrite the original file
                        if($key == 'normal'){
                            $thumbSaveAs = $saveAs;
                        // Otherwise, set the thumb filename to thumb.$key.$filename.$ext
                        } else {
                            $thumbSaveAs = $options['dir'].DS.'thumb.'.$key.'.'.$model->data[$model->name][$fieldName]['name'];
                        }
                        $this->createthumb($saveAs, $thumbSaveAs, $value['width'], $value['height']);
                    }
                }

                // Update model data
                $model->data[$model->name][$options['fields']['dir']] = $options['dir'];
                $model->data[$model->name][$options['fields']['mimetype']] =  $model->data[$model->name][$fieldName]['type'];
                $model->data[$model->name][$options['fields']['filesize']] = $model->data[$model->name][$fieldName]['size'];
                $model->data[$model->name][$fieldName] = $model->data[$model->name][$fieldName]['name'];
            }
            return true;
        }

        /**
         * Deletes the files marked to be deleted in the save method. A file can be marked to be deleted if it is overwriten by another or if the user mark it to be deleted.
         *
         * @author Vinicius Mendes
         * @param $model Object
         */
        function afterSave(&$model) {
            foreach($this->__filesToRemove as $file){
                if($file['name'])
                    $this->_deleteFiles($file['name'], $file['dir']);
            }
            // Reset the filesToRemove array
            $this->__filesToRemove[$model->name] = array();
        }

        /**
         * Deletes all files associated with the record beforing delete it.
         *
         * @author Vinicius Mendes
         * @param $model Object
         */
        function beforeDelete(&$model) {
            $model->read(null, $model->id);
            if(isset($model->data)) {
                foreach($this->__fields[$model->name] as $field=>$options) {
                    $file = $model->data[$model->name][$field];
                    if($file && $file != $options['default'])
                        $this->_deleteFiles($file, $options['dir']);
                }
            }
            return true;
        }

        /**
         * Delete the $filename inside the $dir and the thumbnails.
         * Returns true if the file is deleted and false otherwise.
         *
         * @author Vinicius Mendes
         * @return boolean
         * @param $filename Object
         * @param $dir Object
         */
        function _deleteFiles($filename, $dir){
            $saveAs = $dir . DS . $filename;
            if(is_file($saveAs) && !unlink($saveAs))
            {
                return false;
            }
            $folder = &new Folder($dir);
            $files = $folder->find('thumb\.[a-zA-Z0-9]+\.'.$filename);
            foreach($files as $f) unlink($dir.DS.$f);
            return true;
        }

        // Function to create thumbnail image
        // This requires Nate Constant's thumbnail generator for PHPThumb
        // http://bakery.cakephp.org/articles/view/phpthumb-component
        // Thi function is original from digital spaghetti's version
        function createthumb($name, $filename, $new_w, $new_h)
        {
            App::import('Component', 'Thumb');
            $system = explode(".", $name);

            if (preg_match("/jpg|jpeg/", $system[1]))
            {
                $src_img = imagecreatefromjpeg($name);
            }

            if (preg_match("/png/", $system[1]))
           {
               $src_img = imagecreatefrompng($name);
           }

           $old_x = imagesx($src_img);
           $old_y = imagesy($src_img);

           if ($old_x >= $old_y)
           {
               $thumb_w = $new_w;
               $ratio = $old_y / $old_x;
               $thumb_h = $ratio * $new_w;
           } else if ($old_x 
  • Posted 08/21/10 05:28:27 AM
    In case anyone else has the same problems I had with preserving transparency on transparent png thumnails, I fixed it by adding some code into the "createthumb" method.

    The code I used was adapted from http://mediumexposure.com/smart-image-resizing-while-preserving-transparency-php-and-gd-library/
    So... here's the block of code that worked for me - add it in the "createthumb" method, at line 748, just below the line that says "$dst_img = imagecreatetruecolor($thumb_w, $thumb_h);":


    if (preg_match("/png/", $system[1])) {
      $trnprt_indx = imagecolortransparent($src_img);

      // If we have a specific transparent color
      if ($trnprt_indx >= 0) {

        // Get the original image's transparent color's RGB values
        $trnprt_color    = imagecolorsforindex($src_img, $trnprt_indx);

        // Allocate the same color in the new image resource
        $trnprt_indx    = imagecolorallocate($dst_img, $trnprt_color['red'], $trnprt_color['green'], $trnprt_color['blue']);

        // Completely fill the background of the new image with allocated color.
        imagefill($dst_img, 0, 0, $trnprt_indx);

        // Set the background color for new image to transparent
        imagecolortransparent($dst_img, $trnprt_indx);

     
      }
      // Always make a transparent background color for PNGs that don't have one allocated already
      elseif (preg_match("/png/", $system[1])) {

        // Turn off transparency blending (temporarily)
        imagealphablending($dst_img, false);

        // Create a new transparent color for image
        $color = imagecolorallocatealpha($dst_img, 0, 0, 0, 127);

        // Completely fill the background of the new image with allocated color.
        imagefill($dst_img, 0, 0, $color);

        // Restore transparency blending
        imagesavealpha($dst_img, true);
      }
    }

  • Posted 08/21/10 04:52:30 AM
    Hi, thanks for this behaviour, it's great. I'm having one problem with it though - in my thumbnails, transparent parts of my pngs are not being preserved, and come up as black instead.

    Has anyone else had this problem, or does anyone know the solution?

    Thanks in advance.
  • Posted 10/04/09 10:15:33 AM
    this is the best upload image behavior and i use it several times, how to make it multiple upload? thanx :)
  • Posted 08/31/09 04:21:43 AM
    Hi, i tried the behavior from git but error messages are not shown? Any idea?

    Thanks
  • Posted 08/04/09 01:37:41 PM
    GODDAMM! Worked perfectly the first time.

    Thank you all who worked. Congratulations.
  • Posted 05/17/09 07:39:30 PM
    I've fixed some things that were mentioned on both his blog and here. I've also managed to ACTUALLY use phpThumb. Please note that Nate's ThumbComponent is no longer necessary, and you can specify whether you would like to use ImageMagick or GD. There are some other extensions too, so take a look. Comments are welcome.

    http://github.com/josegonzalez/MeioUpload/tree/master
    • Posted 05/19/09 01:42:35 PM
      I've fixed some things that were mentioned on both his blog and here. I've also managed to ACTUALLY use phpThumb. Please note that Nate's ThumbComponent is no longer necessary, and you can specify whether you would like to use ImageMagick or GD. There are some other extensions too, so take a look. Comments are welcome.
      Hi, where we can find the fixed version?
      Thanks in advance.
      • Posted 07/06/09 07:02:18 AM
        I've fixed some things that were mentioned on both his blog and here. I've also managed to ACTUALLY use phpThumb. Please note that Nate's ThumbComponent is no longer necessary, and you can specify whether you would like to use ImageMagick or GD. There are some other extensions too, so take a look. Comments are welcome.
        Hi, where we can find the fixed version?
        Thanks in advance.

        As is customary, I forgot the link. I've updated my comment and am also including a link here.

        http://github.com/josegonzalez/MeioUpload/tree/master
        Feel free to yell if anything breaks horribly :)
  • Posted 05/12/09 09:13:11 AM
    why do we use oldschool methods? :) isnt it easyer to use imagemagic functions?


        function createthumb($path, $dest_path, $width, $height)
        {
            $need_aspect = $width/$height;
            list($img_w, $img_h) = getimagesize($path);
            
            if ($img_h < $height)
                $img_h = $height;
            elseif ($img_w < $width)
                $img_w = $width;
            
            $aspect = $img_w / $img_h;
            
            if ($aspect > $need_aspect)
                $img_w = $img_h * $need_aspect;
            elseif ($aspect <= $need_aspect)
                $img_h = $img_w / $need_aspect;
            
            if ($width == $height)
                exec(CONVERT." $path -gravity North -strip -crop ".$img_w.'x'.$img_h."+0+0 +repage -quality 95 $dest_path");
            else
                exec(CONVERT." $path -gravity Center -strip -crop ".$img_w.'x'.$img_h."+0+0 +repage -quality 95 $dest_path");
            
            exec(CONVERT." $dest_path -resize ".$width."x".$height."\\> $dest_path");
        }
  • Posted 05/12/09 09:10:35 AM

    ...
                // It the file is an image, try to make the thumbnails
                if (count($options['allowed_ext']) > 0 && in_array($model->data[$model->name][$fieldName]['type'], array('image/jpeg', 'image/pjpeg', 'image/png', 'image/gif'))) {
    ...

    it's interesting, but gif is image too, and i need thumbnails for it too:)

    It's writed that you need that thumbPhp for making thumbnails, but your behavior dont use it?.. you just have a function createthumb for making thumbs :)
  • Posted 04/27/09 04:51:59 PM

    'FieldName' => array(
        'rule' => array('uploadCheckFieldName'),
        'check' => true,
        'message' => 'This field was not defined in MeioUploadBehavior.'
    ),
    'Dir' => array(
        'rule' => array('uploadCheckDir'),
        'check' => true,
        'message' => 'Cannot write to the directory.'
    ),
    'Empty' => array(
        'rule' => array('uploadCheckEmpty'),
        'check' => true,
        'on' => 'create',
        'message' => 'The file cannot be empty.'
    ),
    'UploadError' => array(
        'rule' => array('uploadCheckUploadError'),
        'check' => true,
        'message' => 'There was a problem uploading the file'
    ),
    'MaxSize' => array(
        'rule' => array('uploadCheckMaxSize'),
        'check' => true,
        'message' => 'The file exceeds the maximum size.'
    ),
    'InvalidMime' => array(
        'rule' => array('uploadCheckInvalidMime'),
        'check' => true,
        'message' => 'Invalid file type.'
    ),
    'InvalidExt' => array(
        'rule' => array('uploadCheckInvalidExt'),
        'check' => true,
        'message' => 'Invalid file extension.'
    )
  • Posted 04/16/09 10:42:35 AM
    thanks for this great piece of code :)
  • Posted 02/02/09 02:59:32 PM
    Save yourself 2 hours. Make sure a regular file upload works, before you try using this behavior.

    I had some permissions issues and kept getting "Invalid File Extension" back as my error.

    So ensure that you are able to upload a regular file first (through your controller or whatever), THEN drop in this bad boy.

    Works like a charm. My only caveat is that it doesn't display validation errors with _added_ validation from another model.
    Eg.

    I use a Model (Images) with this behavior. Another Model (Product) "hasOne Image". I have custom validation on the "product_id" field for "isUnique" (I'm trying to enforce the hasOne image). If I upload straight through the Images model, everything is fine. If I upload through the Product Model the validation fails, but I don't get the error back.

    A bit frustrating, but for all what this behavior does, it's worth it.
  • Posted 11/10/08 09:26:38 PM
    Hi ,
    Thanks for your Great Work!
    I wonder if it is possible to upload PDF and video files by modifying MeioUpload Behavior.
  • Posted 11/08/08 01:00:37 PM
    Great work! Thank you! But could you please translate error messages to English? Cos I don't understand some of them.

Comments are closed for articles over a year old