diff --git a/app/Http/Controllers/Controller.php b/app/Http/Controllers/Controller.php index 7ff5293c..ac2b67b1 100644 --- a/app/Http/Controllers/Controller.php +++ b/app/Http/Controllers/Controller.php @@ -22,6 +22,7 @@ use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Hash; use Illuminate\View\View; +use Intervention\Image\Facades\Image as InterventionImage; use League\Flysystem\FilesystemException; use Symfony\Component\Console\Output\BufferedOutput; use Symfony\Component\HttpFoundation\StreamedResponse; @@ -156,7 +157,7 @@ class Controller extends BaseController // 是否启用了水印功能,跳过gif图片 if ($image->group->configs->get(GroupConfigKey::IsEnableWatermark) && $image->mimetype !== 'image/gif') { $configs = $image->group->configs->get(GroupConfigKey::WatermarkConfigs); - $contents = (string)$service->stickWatermark($contents, collect($configs))->encode(); + $contents = $service->stickWatermark($contents, collect($configs))->encode()->getEncoded(); } $cacheTtl = (int)$image->group->configs->get(GroupConfigKey::ImageCacheTtl, 0); // 是否启用了缓存 @@ -167,12 +168,21 @@ class Controller extends BaseController } } } catch (FilesystemException $e) { + Utils::e($e, '图片输出时出现异常'); abort(404); } + $mimetype = $image->mimetype; + + // 浏览器无法预览的图片,改为 png 格式输出 + if (in_array($image->extension, ['psd', 'tif', 'bmp'])) { + $mimetype = 'image/png'; + $contents = InterventionImage::make($contents)->encode('png')->getEncoded(); + } + return \response()->stream(function () use ($contents) { echo $contents; - }, headers: ['Content-type' => $image->mimetype]); + }, headers: ['Content-type' => $mimetype]); } public function thumbnail(Request $request): StreamedResponse @@ -190,7 +200,7 @@ class Controller extends BaseController $contents = Cache::get($cacheKey); } else { $stream = $image->filesystem()->readStream($image->pathname); - $img = \Intervention\Image\Facades\Image::make($stream); + $img = InterventionImage::make($stream); $width = $w = $image->width; $height = $h = $image->height; @@ -203,15 +213,16 @@ class Controller extends BaseController $height = (int)($h * $scale); } - $contents = $img->fit($width, $height, fn($constraint) => $constraint->upsize())->encode(); + $contents = $img->fit($width, $height, fn($constraint) => $constraint->upsize())->encode('png'); Cache::rememberForever($cacheKey, fn () => (string)$contents); } } catch (FilesystemException $e) { + Utils::e($e, '图片缩略图输出时出现异常'); abort(404); } return \response()->stream(function () use ($contents) { echo $contents; - }, headers: ['Content-type' => $image->mimetype]); + }, headers: ['Content-type' => 'image/png']); } } diff --git a/composer.json b/composer.json index 9c73d434..4ca254da 100644 --- a/composer.json +++ b/composer.json @@ -12,6 +12,7 @@ "fruitcake/laravel-cors": "^2.0.5", "guzzlehttp/guzzle": "^7.2", "intervention/image": "^2.7", + "intervention/imagecache": "^2.5", "laravel/breeze": "^1.8", "laravel/framework": "^9.0", "laravel/sanctum": "^2.14", diff --git a/composer.lock b/composer.lock index e3d45ee5..0124f7db 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "87004ae9acac8c83e5f70ed79d85415c", + "content-hash": "f55a4d1756ba393dba532f9d31cd9649", "packages": [ { "name": "adbario/php-dot-notation", @@ -2065,6 +2065,73 @@ ], "time": "2021-12-16T16:49:26+00:00" }, + { + "name": "intervention/imagecache", + "version": "2.5.2", + "source": { + "type": "git", + "url": "https://github.com/Intervention/imagecache.git", + "reference": "270d1e72ddff2fc0a6d3c7e6cbc9d23c9ec1e3e4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Intervention/imagecache/zipball/270d1e72ddff2fc0a6d3c7e6cbc9d23c9ec1e3e4", + "reference": "270d1e72ddff2fc0a6d3c7e6cbc9d23c9ec1e3e4", + "shasum": "" + }, + "require": { + "illuminate/cache": "^5.5|~6|~7|~8|~9", + "illuminate/filesystem": "^5.5|~6|~7|~8|~9", + "intervention/image": "~2.2", + "nesbot/carbon": "^2.39", + "opis/closure": "^3.5", + "php": "~7.2|~8" + }, + "require-dev": { + "phpunit/phpunit": "^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Intervention\\Image\\": "src/Intervention/Image" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Oliver Vogel", + "email": "oliver@intervention.io", + "homepage": "http://intervention.io/" + } + ], + "description": "Caching extension for the Intervention Image Class", + "homepage": "https://image.intervention.io", + "keywords": [ + "cache", + "gd", + "image", + "imagick", + "laravel" + ], + "support": { + "issues": "https://github.com/Intervention/imagecache/issues", + "source": "https://github.com/Intervention/imagecache/tree/2.5.2" + }, + "funding": [ + { + "url": "https://paypal.me/interventionio", + "type": "custom" + }, + { + "url": "https://github.com/Intervention", + "type": "github" + } + ], + "time": "2022-01-22T11:14:47+00:00" + }, { "name": "laravel/breeze", "version": "v1.8.2", @@ -3565,6 +3632,71 @@ }, "time": "2021-11-30T19:35:32+00:00" }, + { + "name": "opis/closure", + "version": "3.6.3", + "source": { + "type": "git", + "url": "https://github.com/opis/closure.git", + "reference": "3d81e4309d2a927abbe66df935f4bb60082805ad" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/opis/closure/zipball/3d81e4309d2a927abbe66df935f4bb60082805ad", + "reference": "3d81e4309d2a927abbe66df935f4bb60082805ad", + "shasum": "" + }, + "require": { + "php": "^5.4 || ^7.0 || ^8.0" + }, + "require-dev": { + "jeremeamia/superclosure": "^2.0", + "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.6.x-dev" + } + }, + "autoload": { + "files": [ + "functions.php" + ], + "psr-4": { + "Opis\\Closure\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Marius Sarca", + "email": "marius.sarca@gmail.com" + }, + { + "name": "Sorin Sarca", + "email": "sarca_sorin@hotmail.com" + } + ], + "description": "A library that can be used to serialize closures (anonymous functions) and arbitrary objects.", + "homepage": "https://opis.io/closure", + "keywords": [ + "anonymous functions", + "closure", + "function", + "serializable", + "serialization", + "serialize" + ], + "support": { + "issues": "https://github.com/opis/closure/issues", + "source": "https://github.com/opis/closure/tree/3.6.3" + }, + "time": "2022-01-27T09:35:39+00:00" + }, { "name": "overtrue/flysystem-cos", "version": "5.0.1", diff --git a/resources/views/components/upload.blade.php b/resources/views/components/upload.blade.php index c5282dfc..d1b4a0db 100644 --- a/resources/views/components/upload.blade.php +++ b/resources/views/components/upload.blade.php @@ -161,28 +161,37 @@ } let guid = utils.guid(); data.guid = guid; - loadImage(file, function (img) { - if (img.type === 'error') { - toastr.error(`文件 ${file.name} 缩略图生成失败`); - console.error('Error loading image file') - } - let html = $('#image-preview-tpl') - .html() - .replace(/__id__/g, guid) - .replace(/__src__/g, img.toDataURL()) - .replace(/__name__/g, file.name) - .replace(/__info__/g, utils.formatSize(file.size)); - data.$preview = $previews.append(html).show().find(`[data-id="${guid}"]`); - queue[guid] = data; - setStatus(data, UPLOAD_WAITING); - }, { - maxWidth: 200, - maxHeight: 200, - meta: true, - orientation: true, - canvas: true - }, - ) + + let setThumb = function (blob) { + let html = $('#image-preview-tpl') + .html() + .replace(/__id__/g, guid) + .replace(/__src__/g, blob) + .replace(/__name__/g, file.name) + .replace(/__info__/g, utils.formatSize(file.size)); + data.$preview = $previews.append(html).show().find(`[data-id="${guid}"]`); + queue[guid] = data; + setStatus(data, UPLOAD_WAITING); + }; + + if (['psd', 'tif'].indexOf(file.name.substring(file.name.lastIndexOf(".") + 1).toLowerCase()) !== -1) { + setThumb('data:image/png;base64,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'); + } else { + loadImage(file, function (img) { + if (img.type === 'error') { + toastr.error(`文件 ${file.name} 缩略图生成失败`); + console.error('Error loading image file') + } + setThumb(img.toDataURL()) + }, { + maxWidth: 200, + maxHeight: 200, + meta: true, + orientation: true, + canvas: true + }, + ) + } }, send: (e, data) => { data.$preview.find('[data-operate="upload"]').hide();