I wanted to add a possibility to save images by distributed directories.
Here’s what came of it. Maybe it will be interesting for someone.
As result I get such example of directories structure (by default I use 4 directories depth):
(I can’t paste link so add a www manually) dl.dropbox.com/u/4490572/imageModuleDirectoriesStructure.png
Below only changed parts of code and all changed files in the archive.
ImgManager.php
<?php
class ImgManager extends CApplicationComponent
{
...
/**
* Enable or not autopath by filename hash.
*
* @property bool
*/
public $autoPath=true;
/**
* Define folders level to store images in the autoPath=true mode.
* .htaccess contain the same value in the row RewriteRule ^(?:\w\w/){4}
*
* @property integer
*/
public $folderDepth=4;
/**
* Image model
*
* @var Image
*/
private $_model;
/**
* Used to store original image path
*
* @var string
*/
private $_originalImagePath;
/**
* Initializes the component.
*/
public function init()
{
self::$_thumbOptions=$this->thumbOptions;
self::$_imagePath=$this->getImagePath(true);
if($this->_originalImagePath===null)
$this->_originalImagePath=$this->imagePath;
parent::init();
}
/**
* Saves a new image.
* @param CUploadedFile $file the uploaded image.
* @param CActiveRecord $parent the parent.
* @param integer $parentId the parent id.
* @return Image the image record.
* @throws ImageException if saving the image record or file fails.
*/
public function save($file,$parent,$parentId)
{
...
$image->parentId=$parentId;
$image->fileHash=md5_file($file->getTempName());
$image->filename=$file->getName();
...
}
/**
* Loads a specific image model.
* @param integer $id the image id.
* @return Image
*/
public function loadModel($id)
{
if($this->_model===null)
$this->_model=Image::model()->findByPk($id);
return $this->_model;
}
/**
* Returns the original image file name.
* @param Image $image the image model.
* @return string the file name.
*/
private function resolveFileName($image)
{
if($image instanceof Image)
{
$this->_model=$image;
if($this->autoPath)
$this->resolveAutoPath();
return $image->id.'.'.$image->extension;
}
else
return null;
}
/**
* Returns the base path.
* @return string the path.
*/
private function getBasePath()
{
if($this->_basePath!==null)
return $this->_basePath;
else
return $this->_basePath=realpath(Yii::getPathOfAlias('webroot')).'/'; // I use different from original directory structure, else this can be without changes
}
/**
* Create or find directory structure for autoPath
*
* @param CUploadedFile $file
*/
private function resolveAutoPath($file=null)
{
static $HASHES=array();
if($file===null)
$hash=$this->_model->fileHash;
else if($file instanceof CUploadedFile)
{
$filePath=$file->getTempName();
if(!file_exists($filePath))
throw new CException(Img::t('error','The image {file} is not exist',array('{file}'=>$filePath)));
$hash=md5_file($filePath);
}
else
throw new CException(Img::t('error','{param} must be an instance of the {className} class.',array('{param}'=>'$file','{className}'=>'CUploadedFile')));
if(!isset($HASHES[$hash]))
{
$this->_versionBasePath=null;
$this->imagePath=$this->_originalImagePath;
$dir=$this->getImagePath(true);
$autoImagePath='';
for($i=0;$i<$this->folderDepth;$i++)
{
$hashPath=substr($hash,$i*2,2).'/';
$dir.=$hashPath;
if(!is_dir($dir))
mkdir($dir);
$autoImagePath.=$hashPath;
}
// Reassign static value
self::$_imagePath=$dir;
$HASHES[$hash]=$this->imagePath.$autoImagePath;
}
$this->imagePath=$HASHES[$hash];
$versionsPath=$this->getVersionBasePath(true);
if(!is_dir($versionsPath))
mkdir($versionsPath);
}
}
Image.php
<?php
class Image extends CActiveRecord
{
...
/**
* @return array validation rules for model attributes.
*/
public function rules()
{
return array(
array('fileHash, filename, extension, byteSize, mimeType', 'required'),
array('fileHash', 'length', 'max'=>32),
array('parentId, byteSize', 'numerical', 'integerOnly'=>true),
...
}
/**
* @return array customized attribute labels (name=>label)
*/
public function attributeLabels()
{
return array(
...
'parent' => Img::t('core', 'Parent Type'),
'fileHash'=>Img::t('core','File Hash'),
'filename' => Img::t('core','Filename'),
...
);
}
Also is you use parameter createOnDemand is set to true, the WEBROOT/images/.htaccess file must be replaced by this:
Options -Indexes
<IfModule mod_rewrite.c>
RewriteEngine on
RewriteBase /
# If the requested file or directory does not exist...
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
# ...and if the source URL points to an image, we redirect to the create image URL.
RewriteRule ^(?:\w\w/){4}versions/([a-zA-Z0-9]+)/([0-9]+)\.(gif|jpg|png)$ image/default/create?id=$2&version=$1 [L,R,QSA]
</IfModule>
Forgot also one moment!
In the table Image I added a new field
`fileHash` CHAR(32) NOT NULL