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); } } } }