mirror of
https://github.com/BookStackApp/BookStack.git
synced 2025-04-12 16:08:08 +00:00
Images: Reverted some thumbnails to be on-demand generated
Added since we can't always be sure of future image usage, and in many cases we don't generate ahead-of-time. Also: - Simplified image handling on certain models. - Updated various string handling operations to use newer functions.
This commit is contained in:
parent
5af3041b9b
commit
5c318a45b8
5 changed files with 38 additions and 49 deletions
app
|
@ -40,26 +40,19 @@ class Book extends Entity implements HasCoverImage
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns book cover image, if book cover not exists return default cover image.
|
* Returns book cover image, if book cover not exists return default cover image.
|
||||||
*
|
|
||||||
* @param int $width - Width of the image
|
|
||||||
* @param int $height - Height of the image
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public function getBookCover($width = 440, $height = 250)
|
public function getBookCover(int $width = 440, int $height = 250): string
|
||||||
{
|
{
|
||||||
$default = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
|
$default = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
|
||||||
if (!$this->image_id) {
|
if (!$this->image_id || !$this->cover) {
|
||||||
return $default;
|
return $default;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$cover = $this->cover ? url($this->cover->getThumb($width, $height, false)) : $default;
|
return $this->cover->getThumb($width, $height, false) ?? $default;
|
||||||
} catch (Exception $err) {
|
} catch (Exception $err) {
|
||||||
$cover = $default;
|
return $default;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $cover;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
namespace BookStack\Entities\Models;
|
namespace BookStack\Entities\Models;
|
||||||
|
|
||||||
use BookStack\Uploads\Image;
|
use BookStack\Uploads\Image;
|
||||||
|
use Exception;
|
||||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||||
|
@ -49,28 +50,21 @@ class Bookshelf extends Entity implements HasCoverImage
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns BookShelf cover image, if cover does not exists return default cover image.
|
* Returns shelf cover image, if cover not exists return default cover image.
|
||||||
*
|
|
||||||
* @param int $width - Width of the image
|
|
||||||
* @param int $height - Height of the image
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
*/
|
||||||
public function getBookCover($width = 440, $height = 250)
|
public function getBookCover(int $width = 440, int $height = 250): string
|
||||||
{
|
{
|
||||||
// TODO - Make generic, focused on books right now, Perhaps set-up a better image
|
// TODO - Make generic, focused on books right now, Perhaps set-up a better image
|
||||||
$default = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
|
$default = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
|
||||||
if (!$this->image_id) {
|
if (!$this->image_id || !$this->cover) {
|
||||||
return $default;
|
return $default;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$cover = $this->cover ? url($this->cover->getThumb($width, $height, false)) : $default;
|
return $this->cover->getThumb($width, $height, false) ?? $default;
|
||||||
} catch (\Exception $err) {
|
} catch (Exception $err) {
|
||||||
$cover = $default;
|
return $default;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $cover;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -45,13 +45,14 @@ class Image extends Model
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get an (already existing) thumbnail for this image.
|
* Get a thumbnail URL for this image.
|
||||||
|
* Attempts to generate the thumbnail if not already existing.
|
||||||
*
|
*
|
||||||
* @throws \Exception
|
* @throws \Exception
|
||||||
*/
|
*/
|
||||||
public function getThumb(?int $width, ?int $height, bool $keepRatio = false): ?string
|
public function getThumb(?int $width, ?int $height, bool $keepRatio = false): ?string
|
||||||
{
|
{
|
||||||
return app()->make(ImageService::class)->getThumbnail($this, $width, $height, $keepRatio);
|
return app()->make(ImageService::class)->getThumbnail($this, $width, $height, $keepRatio, false, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -21,23 +21,18 @@ use Intervention\Image\Exception\NotSupportedException;
|
||||||
use Intervention\Image\Image as InterventionImage;
|
use Intervention\Image\Image as InterventionImage;
|
||||||
use Intervention\Image\ImageManager;
|
use Intervention\Image\ImageManager;
|
||||||
use League\Flysystem\WhitespacePathNormalizer;
|
use League\Flysystem\WhitespacePathNormalizer;
|
||||||
use Psr\SimpleCache\InvalidArgumentException;
|
|
||||||
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
use Symfony\Component\HttpFoundation\File\UploadedFile;
|
||||||
use Symfony\Component\HttpFoundation\StreamedResponse;
|
use Symfony\Component\HttpFoundation\StreamedResponse;
|
||||||
|
|
||||||
class ImageService
|
class ImageService
|
||||||
{
|
{
|
||||||
protected ImageManager $imageTool;
|
|
||||||
protected Cache $cache;
|
|
||||||
protected FilesystemManager $fileSystem;
|
|
||||||
|
|
||||||
protected static array $supportedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
|
protected static array $supportedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
|
||||||
|
|
||||||
public function __construct(ImageManager $imageTool, FilesystemManager $fileSystem, Cache $cache)
|
public function __construct(
|
||||||
{
|
protected ImageManager $imageTool,
|
||||||
$this->imageTool = $imageTool;
|
protected FilesystemManager $fileSystem,
|
||||||
$this->fileSystem = $fileSystem;
|
protected Cache $cache
|
||||||
$this->cache = $cache;
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -206,7 +201,7 @@ class ImageService
|
||||||
* Save image data for the given path in the public space, if possible,
|
* Save image data for the given path in the public space, if possible,
|
||||||
* for the provided storage mechanism.
|
* for the provided storage mechanism.
|
||||||
*/
|
*/
|
||||||
protected function saveImageDataInPublicSpace(Storage $storage, string $path, string $data)
|
protected function saveImageDataInPublicSpace(Storage $storage, string $path, string $data): void
|
||||||
{
|
{
|
||||||
$storage->put($path, $data);
|
$storage->put($path, $data);
|
||||||
|
|
||||||
|
@ -269,8 +264,14 @@ class ImageService
|
||||||
*
|
*
|
||||||
* @throws Exception
|
* @throws Exception
|
||||||
*/
|
*/
|
||||||
public function getThumbnail(Image $image, ?int $width, ?int $height, bool $keepRatio = false, bool $shouldCreate = false): ?string
|
public function getThumbnail(
|
||||||
{
|
Image $image,
|
||||||
|
?int $width,
|
||||||
|
?int $height,
|
||||||
|
bool $keepRatio = false,
|
||||||
|
bool $shouldCreate = false,
|
||||||
|
bool $canCreate = false,
|
||||||
|
): ?string {
|
||||||
// Do not resize GIF images where we're not cropping
|
// Do not resize GIF images where we're not cropping
|
||||||
if ($keepRatio && $this->isGif($image)) {
|
if ($keepRatio && $this->isGif($image)) {
|
||||||
return $this->getPublicUrl($image->path);
|
return $this->getPublicUrl($image->path);
|
||||||
|
@ -305,7 +306,7 @@ class ImageService
|
||||||
return $this->getPublicUrl($image->path);
|
return $this->getPublicUrl($image->path);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$shouldCreate) {
|
if (!$shouldCreate && !$canCreate) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -559,7 +560,7 @@ class ImageService
|
||||||
// Check the image file exists
|
// Check the image file exists
|
||||||
&& $disk->exists($imagePath)
|
&& $disk->exists($imagePath)
|
||||||
// Check the file is likely an image file
|
// Check the file is likely an image file
|
||||||
&& strpos($disk->mimeType($imagePath), 'image/') === 0;
|
&& str_starts_with($disk->mimeType($imagePath), 'image/');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -568,14 +569,14 @@ class ImageService
|
||||||
*/
|
*/
|
||||||
protected function checkUserHasAccessToRelationOfImageAtPath(string $path): bool
|
protected function checkUserHasAccessToRelationOfImageAtPath(string $path): bool
|
||||||
{
|
{
|
||||||
if (strpos($path, '/uploads/images/') === 0) {
|
if (str_starts_with($path, '/uploads/images/')) {
|
||||||
$path = substr($path, 15);
|
$path = substr($path, 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Strip thumbnail element from path if existing
|
// Strip thumbnail element from path if existing
|
||||||
$originalPathSplit = array_filter(explode('/', $path), function (string $part) {
|
$originalPathSplit = array_filter(explode('/', $path), function (string $part) {
|
||||||
$resizedDir = (strpos($part, 'thumbs-') === 0 || strpos($part, 'scaled-') === 0);
|
$resizedDir = (str_starts_with($part, 'thumbs-') || str_starts_with($part, 'scaled-'));
|
||||||
$missingExtension = strpos($part, '.') === false;
|
$missingExtension = !str_contains($part, '.');
|
||||||
|
|
||||||
return !($resizedDir && $missingExtension);
|
return !($resizedDir && $missingExtension);
|
||||||
});
|
});
|
||||||
|
@ -641,9 +642,9 @@ class ImageService
|
||||||
$url = ltrim(trim($url), '/');
|
$url = ltrim(trim($url), '/');
|
||||||
|
|
||||||
// Handle potential relative paths
|
// Handle potential relative paths
|
||||||
$isRelative = strpos($url, 'http') !== 0;
|
$isRelative = !str_starts_with($url, 'http');
|
||||||
if ($isRelative) {
|
if ($isRelative) {
|
||||||
if (strpos(strtolower($url), 'uploads/images') === 0) {
|
if (str_starts_with(strtolower($url), 'uploads/images')) {
|
||||||
return trim($url, '/');
|
return trim($url, '/');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -658,7 +659,7 @@ class ImageService
|
||||||
|
|
||||||
foreach ($potentialHostPaths as $potentialBasePath) {
|
foreach ($potentialHostPaths as $potentialBasePath) {
|
||||||
$potentialBasePath = strtolower($potentialBasePath);
|
$potentialBasePath = strtolower($potentialBasePath);
|
||||||
if (strpos(strtolower($url), $potentialBasePath) === 0) {
|
if (str_starts_with(strtolower($url), $potentialBasePath)) {
|
||||||
return 'uploads/images/' . trim(substr($url, strlen($potentialBasePath)), '/');
|
return 'uploads/images/' . trim(substr($url, strlen($potentialBasePath)), '/');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -679,7 +680,7 @@ class ImageService
|
||||||
// region-based url will be used to prevent http issues.
|
// region-based url will be used to prevent http issues.
|
||||||
if (!$storageUrl && config('filesystems.images') === 's3') {
|
if (!$storageUrl && config('filesystems.images') === 's3') {
|
||||||
$storageDetails = config('filesystems.disks.s3');
|
$storageDetails = config('filesystems.disks.s3');
|
||||||
if (strpos($storageDetails['bucket'], '.') === false) {
|
if (!str_contains($storageDetails['bucket'], '.')) {
|
||||||
$storageUrl = 'https://' . $storageDetails['bucket'] . '.s3.amazonaws.com';
|
$storageUrl = 'https://' . $storageDetails['bucket'] . '.s3.amazonaws.com';
|
||||||
} else {
|
} else {
|
||||||
$storageUrl = 'https://s3-' . $storageDetails['region'] . '.amazonaws.com/' . $storageDetails['bucket'];
|
$storageUrl = 'https://s3-' . $storageDetails['region'] . '.amazonaws.com/' . $storageDetails['bucket'];
|
||||||
|
|
|
@ -244,7 +244,7 @@ class User extends Model implements AuthenticatableContract, CanResetPasswordCon
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$avatar = $this->avatar ? url($this->avatar->getThumb($size, $size, false)) : $default;
|
$avatar = $this->avatar?->getThumb($size, $size, false) ?? $default;
|
||||||
} catch (Exception $err) {
|
} catch (Exception $err) {
|
||||||
$avatar = $default;
|
$avatar = $default;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue