kuvalda-file/File.php

659 lines
21 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace dominion\file;
use Yii;
use yii\web\UploadedFile;
use Imagine\Image\Box;
use Imagine\Image\ImageInterface;
use \yii\imagine\Image;
/**
* This is the model class for table "file".
*
* @property int $id
* @property string $date
* @property string $module
* @property int $height
* @property int $width
* @property int $fileSize
* @property string $md5
* @property string $ext
* @property string $description
* @property string $originalName
*/
class File extends \yii\db\ActiveRecord
{
public $file;
public $cropFile = false;
public $convertJpg = true;
/**
* {@inheritdoc}
*/
public static function tableName()
{
return 'file';
}
/**
* {@inheritdoc}
*/
public function rules()
{
return [
[['date'], 'safe'],
[['module'], 'required'],
[['height', 'width', 'fileSize'], 'integer'],
[['description'], 'string'],
[['module', 'originalName', 'name'], 'string', 'max' => 500],
[['md5', 'ext'], 'string', 'max' => 50],
];
}
/**
* {@inheritdoc}
*/
/*public function attributeLabels()
{
return [
'id' => Module::t('app', 'ID'),
'date' => Module::t('app', 'Date'),
'module' => Module::t('app', 'Module'),
'height' => Module::t('app', 'Height'),
'width' => Module::t('app', 'Width'),
'fileSize' => Module::t('app', 'File Size'),
'md5' => Module::t('app', 'Md5'),
'ext' => Module::t('app', 'Ext'),
'description' => Module::t('app', 'Description'),
'originalName' => Module::t('app', 'Original Name'),
'name' => Module::t('app', 'Name'),
];
}*/
public static function validateFormat($model, $attribute, $params, $fileName = 'file')
{
$fileModel = new File;
$fileModel->file = UploadedFile::getInstance($fileModel, $fileName);
if ($fileModel->file && $fileModel->file->tempName)
{
\yii\validators\Validator::createValidator('file', $fileModel, 'file', $params)->validateAttribute($fileModel, 'file');
if ($fileModel->hasErrors())
{
$model->addError($attribute, 'Не верный формат файла');
}
}
}
/**
* Папка с файлом
* @param boolian $full
* @return type
*/
public function getDir($full = false, $resize = false)
{
$dir = $resize ? 'file_resize' : 'file';
$output = '';
// $full = $full && !Yii::$app->has('s3');
if($full)
{
$output = Yii::getAlias('@app/web');
}
$output .= '/data/' . $dir . '/' . $this->module . '/' . wordwrap(mb_substr($this->md5, 0, 6), 2, '/', true) . '/';
if(Yii::$app->has('s3') && $full && $resize)
{
//если работаем с облаком не плодим папки миниатюр
$output = Yii::getAlias('@app/web/data/file_cache/');
}
if ($full && !file_exists($output))
{
mkdir($output, 0755, true);
}
if(Yii::$app->has('s3') && $full && $resize)
{
//если работаем с облаком не плодим папки миниатюр
$output .= $this->md5.'-';
}
return $output;
}
/**
* Путь к папке кеша
* @return string
*/
public function getDirCache()
{
$dir = Yii::getAlias('@app/web/data/file_cache/');
if (!file_exists($dir))
{
mkdir($dir, 0755, true);
}
return $dir;
}
public function getFilePath($type = false, $full = false)
{
$pref = $type ? ($type . '-') : '';
return $this->getDir($full, $type != false) . $pref . $this->name . '.' . $this->ext;
}
public function getFilePathCache()
{
$this->cropFile = $this->cropFile ? $this->cropFile :$this->getDirCache() . rand(1000, 999999);
return $this->cropFile;
}
public function getIsImage()
{
return in_array($this->ext, ['jpg', 'png', 'jpeg']);
}
public function getShowImage()
{
return in_array($this->ext, ['jpg', 'png', 'jpeg', 'svg']);
}
protected function getFileName()
{
$defaultName = empty($this->name) ? $this->md5 : $this->name;
$name = $defaultName;
$i = 0;
while(($model = File::find()->andWhere(['ext' => $this->ext, 'name'=>$name])->andWhere('md5 like "'. mb_substr($this->md5, 0, 6).'%"')->one()) !== null)
{
$i++;
$name = $defaultName . '-' .$i;
}
return $name;
}
/**
* {@inheritdoc}
*/
public function beforeSave($insert)
{
if ($this->file)
{
$this->ext = $this->file->extension;
$this->md5 = md5_file($this->file->tempName);
$patch = $this->getDir(true);
if ($this->isImage)
{
$this->ext = $this->convertJpg ? 'jpg' : $this->ext;
$this->name = $this->getFileName();
$fileName = $this->name . '.' . $this->ext;
$image = Image::getImagine()->open($this->file->tempName);
$size = $image->getSize();
$this->width = $size->getWidth();
$this->height = $size->getHeight();
$box = new Box($this->width, $this->height);
$palette = new \Imagine\Image\Palette\RGB();
$color = $palette->color(Image::$thumbnailBackgroundColor, Image::$thumbnailBackgroundAlpha);
$thumb = Image::getImagine()->create($box, $color);
$thumb->paste($image, new \Imagine\Image\Point(0, 0));
// var_dump($thumb); die();
$thumb->save($patch . $fileName);
$this->fileSize = filesize($patch . $fileName);
}
else
{
$this->name = $this->getFileName();
$this->fileSize = $this->file->size;
$fileName = $this->name . '.' . $this->ext;
if (is_uploaded_file($this->file->tempName))
{
$this->file->saveAs($patch . $fileName);
}
else
{
rename($this->file->tempName, $patch . $fileName);
}
}
$this->originalName = $this->file->name;
$this->date = date('Y-m-d H:i:s');
}
return parent::beforeSave($insert);
}
public function afterSave($insert, $changedAttributes)
{
if (isset($this->file))
{
$configs = Config::find()->andWhere(['module' => $this->module])->all();
foreach ($configs as $config)
{
$fileTemplate = $config->file ? [
'filePatch' => $config->file->getFilePath(false, true),
'border_top' => $config->border_top,
'border_right' => $config->border_right,
'border_left' => $config->border_left,
'border_bottom' => $config->border_bottom,
] : [];
if($config->file)
{
$config->file->downloadOriginal();
}
$quality = isset($config->quality) ? $config->quality : 90;
$this->resize($config->type, $config->height, $config->width, $config->crop, $config->cut, $config->addBorder, true, $fileTemplate, $quality);
}
if($this->cropFile)
{
@unlink($this->cropFile);
}
$this->moveToS3();
}
return parent::afterSave($insert, $changedAttributes);
}
/**
* Возврашает путь к изображению
* @param type $id
* @return string
*/
public static function getPath($id, $type = false)
{
$output = '';
if ($id > 0 && ($model = File::findOne($id)) !== null)
{
$output = $model->getFilePath($type);
}
return $output;
}
/**
* Возврашает путь к изображению
* @param type $id
* @return string
*/
public static function getPathFull($id, $type = false, $default = false)
{
$img = File::getPath($id, $type);
$fileUrlDomen = isset(Yii::$app->params['fileUrlDomen']) ? Yii::$app->params['fileUrlDomen'] : '';
$img = empty($img) ? $default : $img;
return empty($img) ? false : (preg_replace("#/$#", "", $fileUrlDomen) . $img);
}
public static function getPathsFull($id, $types = [])
{
$fileUrlDomen = isset(Yii::$app->params['fileUrlDomen']) ? Yii::$app->params['fileUrlDomen'] : '';
return self::getPaths($id, $types, $fileUrlDomen);
}
/**
* Возврашает пути к изображениям
* @param type $id
* @return array
*/
public static function getPaths($id, $types = [], $prefix = '')
{
$output = [];
if ($id > 0 && ($model = File::findOne($id)) !== null)
{
foreach($types as $type)
{
$output[$type] = $prefix. $model->getFilePath($type);
}
}
return $output;
}
protected function deleteDisckFile($deleteOriginal = false)
{
if($deleteOriginal)
{
//удаляем оригинал
$file = $this->getFilePath(false, true);
if(file_exists($file))
{
unlink($file);
$files = array_diff(scandir($this->getDir(true)), array('.', '..'));
if(empty($files))
{
Helper::delFolder($this->getDir(true));
}
}
}
//удаляем миниатюры
$dir = $this->getDir(true, true);
if(file_exists($dir))
{
$files = array_diff(scandir($dir), array('.', '..'));
foreach ($files as $key => $value)
{
if(stripos($value, $this->md5 . '.' . $this->ext) !== false)
{
unlink("$dir/$value");
unset($files[$key]);
}
}
if(empty($files))
{
Helper::delFolder($this->getDir(true, true));
}
}
}
protected function deleteS3File()
{
if(Yii::$app->has('s3'))
{
$s3 = Yii::$app->get('s3');
$s3->delete($this->getFilePath());
$configs = Config::find()->andWhere(['module' => $this->module])->all();
foreach ($configs as $config)
{
$s3->delete($this->getFilePath($config->type));
}
}
}
public function delete()
{
$this->deleteDisckFile(true);
$this->deleteS3File();
return parent::delete();
}
/**
* Создание миниатюр
* @param string $type
* @param int $height
* @param int $width
* @param boolian $crop обрезать белую рамку
* @param boolian $cut обрезать по расмеру без соблюдения пропорций сторон
* @param boolian $addBorder добавить белую рамку
* @param boolian $all обновить изображение если уже оно создано
* @param array $fileTemplate Вписать в шаблон
* @param int $quality качество (степень сжатия)
*
*/
public function resize($type, $height, $width, $crop = false, $cut = false, $addBorder = false, $all = false, $fileTemplate = [], $quality = 90)
{
$patch = $this->getDir(true);
$fileName = $this->name . '.' . $this->ext;
$filePatch = $patch . $fileName;
$quality = $quality > 100 || $quality <= 0 ? 90 : $quality;
if (file_exists($filePatch) && in_array(exif_imagetype($filePatch), [IMAGETYPE_JPEG, IMAGETYPE_PNG]))
{
$resizePatch = $this->getDir(true, true);
$resizeFileName = $type . '-' . $fileName;
if ($all || !file_exists($resizePatch . $resizeFileName))
{
if ($crop)
{
$filePatch = $this->cropFile ? $this->cropFile : $this->deleteBorder($filePatch);
}
if(!empty($fileTemplate) && isset($fileTemplate['filePatch'], $fileTemplate['border_top'], $fileTemplate['border_right'], $fileTemplate['border_left'], $fileTemplate['border_bottom']))
{
\dominion\file\Image::thumbnailFileTemplate($filePatch, $width, $height, $fileTemplate)->save($resizePatch . $resizeFileName, ['quality' => $quality]);
}
elseif ($cut || $addBorder)
{
$class = $addBorder ? '\dominion\file\Image' : '\yii\imagine\Image';
$mode = $addBorder ? ImageInterface::THUMBNAIL_INSET : ImageInterface::THUMBNAIL_OUTBOUND;
$class::thumbnail($filePatch, $width, $height, $mode)->save($resizePatch . $resizeFileName, ['quality' => $quality]);
}
else
{
Image::getImagine()->open($filePatch)->thumbnail(new Box($width, $height))->save($resizePatch . $resizeFileName, ['quality' => $quality]);
}
}
}
}
/**
* Обрезает белую рамку
* @param type $filePatch
* @return type
*/
protected function deleteBorder($filePatch)
{
$img = imagecreatefromjpeg($filePatch);
$this->width = $this->width > 0 ? $this->width : imagesx($img);
$this->height = $this->height > 0 ? $this->height : imagesy($img);
$border = array(
'left' => $this->getBorderSize($img, $this->width, $this->height, 'r'),
'right' => $this->getBorderSize($img, $this->width, $this->height, 'l'),
'top' => $this->getBorderSize($img, $this->height, $this->width, 'b'),
'bottom' => $this->getBorderSize($img, $this->height, $this->width, 't'),
);
$x = $border['left'];
$y = $border['top'];
$newWidth = $this->width - ($border['left'] + $border['right']);
$newHeight = $this->height - ($border['top'] + $border['bottom']);
if($newWidth == 0 || $newHeight == 0)
{
return $filePatch;
}
// create the working image
$workingImage = function_exists('imagecreatetruecolor') ? imagecreatetruecolor($newWidth, $newHeight) : imagecreate($newWidth, $newHeight);
// and create the newly sized image
imagecopy(
$workingImage,
$img,
0,
0,
$x,
$y,
$newWidth,
$newHeight
);
$temp = $this->getFilePathCache();
imagejpeg($workingImage, $temp, 100);
return $temp;
}
/**
* Возвращает позицию первого не белого пикселя
* @param type $image
* @param type $width
* @param type $height
* @param type $direction
* @return int
* @throws Exception
*/
protected function getBorderSize($image, $width, $height, $direction)
{
$border = 0;
$realWidth = imagesx($image);
$realHeight = imagesy($image);
for ($i = 0; $i < $width; $i++)
{
$isWhite = true;
$errors = round($height / 100);
for ($j = 0; $j < $height; $j++)
{
switch ($direction)
{
case 'l':
$x = $width - $i - 1;
$y = $j;
break;
case 'b':
$x = $j;
$y = $i;
break;
case 't':
$x = $j;
$y = $width - $i - 1;
break;
default:
$x = $i;
$y = $j;
break;
}
if ($x > $realWidth || $y > $realHeight || $x < 0 || $y < 0)
{
throw new Exception('Не верные координаты изображения');
}
// Получаем RGB пикселя по координате
$color = imageColorAt($image, $x, $y);
// Разбиваем RGB на Red,Green,Blue и записываем каждую составляющую в свою переменную
$pixel = imageColorsForIndex($image, $color);
list($r, $g, $b) = array_values($pixel);
//Сравнение белого ли цвета пиксель
if ($r > 241 && $g > 241 && $b > 241)
{
// белый
}
else
{
// не белый
$errors--;
if ($errors <= 0)
{
$isWhite = false;
break;
}
}
}
if ($isWhite)
{
$border++;
}
else
{
break;
}
}
return $border;
}
public function loadFile($module, $url, $id = false)
{
$file = @file_get_contents($url);
if ($file !== false)
{
$temp = $this->getFilePathCache();
file_put_contents($temp, $file);
unset($file);
if (!$id || ($model = File::findOne($id)) === null)
{
$model = new File();
$model->module = $module;
}
$arName = explode('/', $url);
$model->file = new UploadedFile(
[
'name' => array_pop($arName),
'tempName' => $temp,
'type' => mime_content_type($temp),
'size' => filesize($temp),
'error' => UPLOAD_ERR_OK,
]
);
$model->save();
return $model->id;
}
}
/**
* Сохранение файла из строки Base64
* @param type $module
* @param type $fileBase64
* @param type $fileName
* @param type $id
* @return type
*/
public static function saveBase64File($module, $fileBase64, $fileName, $id = false, $convertJpg = true)
{
if (!$id || ($model = File::findOne($id)) === null)
{
$model = new File();
$model->module = $module;
}
$temp = $model->getFilePathCache();
file_put_contents($temp, base64_decode($fileBase64));
$model->file = new UploadedFile(
[
'name' => $fileName,
'tempName' => $temp,
'type' => mime_content_type($temp),
'size' => filesize($temp),
'error' => UPLOAD_ERR_OK,
]
);
$model->convertJpg = $convertJpg;
$model->save();
return $model->id;
}
public function getSize()
{
$fix = array(' B', ' KB', ' MB', ' GB', ' TB');
$round = 2;
$mult = 1E3;
$range = 0;
while (1 < $this->fileSize / pow($mult, $range))
{
$range++;
}
$mult = pow($mult, $range - 1);
$postfix = $fix[$range - 1];
return round($this->fileSize / $mult, $round) . $postfix;
}
public function moveToS3()
{
if(Yii::$app->has('s3'))
{
$s3 = Yii::$app->get('s3');
if(file_exists($this->getFilePath(false, true)))
{
$s3->upload($this->getFilePath(), $this->getFilePath(false, true));
$configs = Config::find()->andWhere(['module' => $this->module])->all();
foreach ($configs as $config)
{
if(file_exists($this->getFilePath($config->type, true)))
{
$s3->upload($this->getFilePath($config->type), $this->getFilePath($config->type, true));
}
}
}
}
//var_dump($this->getFilePath());
}
public function downloadOriginal()
{
if(Yii::$app->has('s3'))
{
$filePatch = $this->getFilePath(false, true);
if (!file_exists($filePatch) && isset(Yii::$app->params['fileUrlDomen']))
{
$file = @file_get_contents(Yii::$app->params['fileUrlDomen']. $this->getFilePath());
if($file)
{
@file_put_contents($filePatch, $file);
}
}
}
}
public static function upload($oldFile, $newFile)
{
if(Yii::$app->has('s3'))
{
$s3 = Yii::$app->get('s3');
if(file_exists($oldFile))
{
$s3->upload($newFile, $oldFile);
}
}
}
}