Skip to content

Instantly share code, notes, and snippets.

@AgelxNash
Last active September 2, 2022 21:48
Show Gist options
  • Save AgelxNash/0b6faaa7978e3456f3cbd3ef06b365da to your computer and use it in GitHub Desktop.
Save AgelxNash/0b6faaa7978e3456f3cbd3ef06b365da to your computer and use it in GitHub Desktop.
Комманда под artisan для сжатия картинок при помощи https://github.com/maksatweb/compressor.io-php
<?php
namespace App\Console\Commands;
use Illuminate\Console\Command;
use App\Models;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use serhatozles\compressio\CompressorIO;
class CompressImages extends Command
{
protected $status = 0;
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'compress:images';
protected $base = '';
protected $pathOffset = 0;
/**
* The console command description.
*
* @var string
*/
protected $description = 'Оптимизация картинок';
public function run(InputInterface $input, OutputInterface $output)
{
$this->setCode([$this, 'handle']);
return parent::run($input, $output);
}
/**
* Execute the console command.
*
* @return mixed
*/
public function handle(InputInterface $input, OutputInterface $output)
{
$this->pathOffset = mb_strlen(public_path());
$compress = new CompressorIO();
$compress->backup = true;
/** @var \Symfony\Component\Finder\SplFileInfo $file */
foreach ($this->files($this->checkDirs()) as $file) {
if (! $file->isWritable() || ! $this->isValidExtension($file->getExtension())) {
$this->output->writeln('Ignore ' . $file->getRealPath());
continue;
}
try {
/** @var Models\Images $imageModel */
$imageModel = Models\Images::firstOrNew(
[
'path' => $this->clearPath($file->getPath()),
'name' => $file->getBasename(
'.' . $file->getExtension()
),
'ext' => $file->getExtension()
]
);
$hash = sha1_file($file->getRealPath());
if ($imageModel->compress_hash === $hash || $imageModel->compress_size === $file->getSize()) {
$this->output->writeln('Skip ' . $imageModel->fullPath);
continue;
}
$imageModel->original_hash = $hash;
$imageModel->original_size = $file->getSize();
$compress->compress([$file->getRealPath()]);
clearstatcache(true, $file->getRealPath());
$imageModel->compress_hash = sha1_file($file->getRealPath());
$imageModel->compress_size = filesize($file->getRealPath());
$this->output->writeLn($imageModel->compressRatio . ' => ' . $imageModel->fullPath);
try {
$imageModel->save();
} catch (\Exception $exception) {
/**
* Не удалось сохранить (видимо имя файла больше или еще какие-то проблемы)
* Но это не страшно, т.к. перед оптимизацией мы проверяем размер и хеш файла
*/
$this->output->warning($exception->getMessage());
}
} catch (\Exception $exception) {
/**
* Скорее всего какой-то косяк в имени файла
* Поэтому оптимизацией мы не будем заниматься
* Но еще возможен косяк в оптимизаторе:-(
*/
$this->output->error($exception->getMessage());
}
}
return $this->status;
}
public function clearPath(string $path)
{
return mb_substr($path, $this->pathOffset);
}
/**
* Список папок в которых будем оптимизировать картинки
*
* @return array
*/
protected function checkDirs() : array
{
return [
public_path('uploads'),
public_path('images'),
public_path('i')
];
}
/**
* Список расширений, которые мы будем оптимизировать
*
* @return array
*/
protected function getExtensions() : array
{
return ['png', 'jpg', 'jpeg', 'gif'];
}
/**
* @param string|array $directory
* @return array|\Generator
*/
protected function files($directory)
{
$finder = \Symfony\Component\Finder\Finder::create()->files()
->ignoreDotFiles(true)
->in($directory);
foreach ($finder as $file) {
yield $file;
}
}
/**
* Будем ли мы оптимизировать файл с таким расширением
*
* @param string $ext
* @return bool
*/
protected function isValidExtension(string $ext) : bool
{
return ! empty($ext) && \in_array(mb_strtolower($ext), $this->getExtensions(), true);
}
}
<?php namespace App\Models;
use Illuminate\Database\Eloquent;
/**
* @property int $id
* @property string $path
* @property string $name
* @property string $ext
* @property string $original_hash
* @property int $original_size
* @property string $compress_hash
* @property int $compress_size
*
* @property-read string $fullPath
* @property-read string $full_path
* @property-read float $compressRatio
* @property-read float $compress_ratio
*/
class Images extends Eloquent\Model
{
/**
* @var string
*/
protected $table = 'images';
/**
* @var array
*/
protected $fillable = [
'path',
'name',
'ext',
'original_hash',
'compress_hash',
'original_size',
'compress_size'
];
protected $casts = [
'path' => 'string',
'name' => 'string',
'ext' => 'string',
'original_hash' => 'string',
'compress_hash' => 'string',
'original_size' => 'int',
'compress_size' => 'int'
];
/**
* @return string
*/
public function getFullPathAttribute() : string
{
return $this->path . '/' . $this->name . '.' . $this->ext;
}
/**
* @return float
*/
public function getCompressRatioAttribute() : float
{
return $this->original_size > 0 ? (100 - round($this->compress_size * 100 / $this->original_size, 2)) : 0;
}
}
<?php
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class CreateImagesTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('images', function (Blueprint $table) {
$table->charset = 'utf8mb4';
$table->collation = 'utf8mb4_general_ci';
$table->increments('id');
$table->string('path');
$table->string('name');
$table->string('ext');
$table->char('original_hash', 40)->index();
$table->char('compress_hash', 40)->index();
$table->bigInteger('original_size');
$table->bigInteger('compress_size');
$table->timestamps();
$table->index(['path', 'name', 'ext']);
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('images');
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment