|
|
@@ -30,13 +30,130 @@ class FileService
|
|
|
throw new RuntimeException('Не удалось сохранить файл. Проверьте имя файла и повторите попытку.', 0, $e);
|
|
|
}
|
|
|
|
|
|
- return File::query()->create([
|
|
|
+ $fileModel = File::query()->create([
|
|
|
'link' => url('/storage/' . $relativePath),
|
|
|
'path' => $relativePath,
|
|
|
'user_id' => auth()->user()->id,
|
|
|
'original_name' => $originalName,
|
|
|
'mime_type' => $file->getClientMimeType(),
|
|
|
]);
|
|
|
+
|
|
|
+ $this->ensureThumbnail($fileModel);
|
|
|
+
|
|
|
+ return $fileModel;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function ensureThumbnail(File $file, bool $overwrite = false): bool
|
|
|
+ {
|
|
|
+ if (!$this->isImageFile($file) || !$file->path || $this->isThumbnailPath($file->path)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ $disk = Storage::disk('public');
|
|
|
+ if (!$disk->exists($file->path)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ $thumbnailPath = $this->thumbnailPath($file->path);
|
|
|
+ if ($disk->exists($thumbnailPath) && !$overwrite) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return $this->createThumbnail($file->path, $thumbnailPath);
|
|
|
+ }
|
|
|
+
|
|
|
+ public function deleteFileWithThumbnail(File $file): void
|
|
|
+ {
|
|
|
+ if ($file->path) {
|
|
|
+ Storage::disk('public')->delete($file->path);
|
|
|
+
|
|
|
+ if (!$this->isThumbnailPath($file->path)) {
|
|
|
+ Storage::disk('public')->delete($this->thumbnailPath($file->path));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ public function thumbnailPath(string $path): string
|
|
|
+ {
|
|
|
+ $directory = pathinfo($path, PATHINFO_DIRNAME);
|
|
|
+ $filename = pathinfo($path, PATHINFO_FILENAME);
|
|
|
+ $extension = pathinfo($path, PATHINFO_EXTENSION);
|
|
|
+ $thumbnailName = $filename . '.thumbnail' . ($extension !== '' ? '.' . $extension : '');
|
|
|
+
|
|
|
+ return $directory === '.' ? $thumbnailName : $directory . '/' . $thumbnailName;
|
|
|
+ }
|
|
|
+
|
|
|
+ public function isThumbnailPath(string $path): bool
|
|
|
+ {
|
|
|
+ return Str::contains(pathinfo($path, PATHINFO_FILENAME), '.thumbnail');
|
|
|
+ }
|
|
|
+
|
|
|
+ public function isImageFile(File $file): bool
|
|
|
+ {
|
|
|
+ if (in_array((string) $file->mime_type, ['image/jpeg', 'image/png', 'image/webp'], true)) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ return in_array(Str::lower(pathinfo((string) $file->path, PATHINFO_EXTENSION)), ['jpg', 'jpeg', 'png', 'webp'], true);
|
|
|
+ }
|
|
|
+
|
|
|
+ private function createThumbnail(string $sourcePath, string $thumbnailPath): bool
|
|
|
+ {
|
|
|
+ if (!extension_loaded('gd')) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ $disk = Storage::disk('public');
|
|
|
+ $sourceFullPath = $disk->path($sourcePath);
|
|
|
+ $imageInfo = @getimagesize($sourceFullPath);
|
|
|
+
|
|
|
+ if (!$imageInfo || empty($imageInfo[0]) || empty($imageInfo[1])) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ [$width, $height, $type] = $imageInfo;
|
|
|
+ $source = match ($type) {
|
|
|
+ IMAGETYPE_JPEG => function_exists('\imagecreatefromjpeg') ? @\imagecreatefromjpeg($sourceFullPath) : false,
|
|
|
+ IMAGETYPE_PNG => function_exists('\imagecreatefrompng') ? @\imagecreatefrompng($sourceFullPath) : false,
|
|
|
+ IMAGETYPE_WEBP => function_exists('\imagecreatefromwebp') ? @\imagecreatefromwebp($sourceFullPath) : false,
|
|
|
+ default => false,
|
|
|
+ };
|
|
|
+
|
|
|
+ if (!$source) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ $maxSide = $this->thumbnailMaxSide();
|
|
|
+ $scale = min(1, $maxSide / max($width, $height));
|
|
|
+ $thumbnailWidth = max(1, (int) round($width * $scale));
|
|
|
+ $thumbnailHeight = max(1, (int) round($height * $scale));
|
|
|
+ $thumbnail = \imagecreatetruecolor($thumbnailWidth, $thumbnailHeight);
|
|
|
+
|
|
|
+ if (in_array($type, [IMAGETYPE_PNG, IMAGETYPE_WEBP], true)) {
|
|
|
+ \imagealphablending($thumbnail, false);
|
|
|
+ \imagesavealpha($thumbnail, true);
|
|
|
+ }
|
|
|
+
|
|
|
+ \imagecopyresampled($thumbnail, $source, 0, 0, 0, 0, $thumbnailWidth, $thumbnailHeight, $width, $height);
|
|
|
+ $disk->makeDirectory(dirname($thumbnailPath));
|
|
|
+ $thumbnailFullPath = $disk->path($thumbnailPath);
|
|
|
+
|
|
|
+ $saved = match ($type) {
|
|
|
+ IMAGETYPE_JPEG => function_exists('\imagejpeg') && \imagejpeg($thumbnail, $thumbnailFullPath, 82),
|
|
|
+ IMAGETYPE_PNG => function_exists('\imagepng') && \imagepng($thumbnail, $thumbnailFullPath, 7),
|
|
|
+ IMAGETYPE_WEBP => function_exists('\imagewebp') && \imagewebp($thumbnail, $thumbnailFullPath, 82),
|
|
|
+ default => false,
|
|
|
+ };
|
|
|
+
|
|
|
+ \imagedestroy($source);
|
|
|
+ \imagedestroy($thumbnail);
|
|
|
+
|
|
|
+ return (bool) $saved;
|
|
|
+ }
|
|
|
+
|
|
|
+ private function thumbnailMaxSide(): int
|
|
|
+ {
|
|
|
+ return max(1, (int) config('filesystems.photo_thumbnail_max_side', 150));
|
|
|
}
|
|
|
|
|
|
private function sanitizeOriginalName(string $originalName): string
|