mirror of
https://github.com/getgrav/grav.git
synced 2026-06-25 21:42:00 +02:00
Postpone image processing to cache image requests
This commit is contained in:
@@ -12,6 +12,8 @@ namespace Grav\Common\Media\Factories;
|
||||
use Grav\Common\Media\Interfaces\MediaCollectionInterface;
|
||||
use Grav\Common\Media\Interfaces\MediaFactoryInterface;
|
||||
use Grav\Common\Page\Media;
|
||||
use Grav\Common\Utils;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -38,4 +40,40 @@ class LocalMediaFactory implements MediaFactoryInterface
|
||||
|
||||
return new Media($path, $order, $load);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param string $path
|
||||
* @return string
|
||||
*/
|
||||
public function readFile(string $type, string $path): string
|
||||
{
|
||||
$filepath = GRAV_WEBROOT . '/' . $path;
|
||||
|
||||
error_clear_last();
|
||||
$contents = @file_get_contents($filepath);
|
||||
if (false === $contents) {
|
||||
throw new RuntimeException('Reading media file failed: ' . (error_get_last()['message'] ?? sprintf('Cannot read %s', Utils::basename($filepath))));
|
||||
}
|
||||
|
||||
return $contents;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $type
|
||||
* @param string $path
|
||||
* @return resource
|
||||
*/
|
||||
public function readStream(string $type, string $path)
|
||||
{
|
||||
$filepath = GRAV_WEBROOT . '/' . $path;
|
||||
|
||||
error_clear_last();
|
||||
$contents = @fopen($filepath, 'rb');
|
||||
if (false === $contents) {
|
||||
throw new RuntimeException('Reading media file failed: ' . (error_get_last()['message'] ?? sprintf('Cannot open %s', Utils::basename($filepath))));
|
||||
}
|
||||
|
||||
return $contents;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ use Grav\Common\Grav;
|
||||
use Grav\Common\Media\Events\MediaEventSubscriber;
|
||||
use Grav\Common\Media\Interfaces\MediaCollectionInterface;
|
||||
use Grav\Common\Media\Interfaces\MediaFactoryInterface;
|
||||
use RuntimeException;
|
||||
use Symfony\Component\EventDispatcher\EventDispatcher;
|
||||
|
||||
/**
|
||||
@@ -96,4 +97,48 @@ final class MediaFactory implements MediaFactoryInterface
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $uri
|
||||
* @return string
|
||||
*/
|
||||
public function readFile(string $uri): string
|
||||
{
|
||||
if (preg_match('{^(?:media-([^:]+)://)?(.*)$}', $uri, $matches)) {
|
||||
$type = str_replace('-', '_', $matches[1]);
|
||||
$filepath = $matches[2];
|
||||
} else {
|
||||
$type = 'local';
|
||||
$filepath = $uri;
|
||||
}
|
||||
|
||||
$factory = $this->collectionTypes[$type] ?? null;
|
||||
if ($factory) {
|
||||
return $factory->readFile($type, $filepath);
|
||||
}
|
||||
|
||||
throw new RuntimeException(sprintf('Reading media file failed: type %s does not exist', $type), 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $uri
|
||||
* @return resource
|
||||
*/
|
||||
public function readStream(string $uri)
|
||||
{
|
||||
if (preg_match('{^(?:media-([^:]+)://)?(.*)$}', $uri, $matches)) {
|
||||
$type = str_replace('-', '_', $matches[1]);
|
||||
$filepath = $matches[2];
|
||||
} else {
|
||||
$type = 'local';
|
||||
$filepath = $uri;
|
||||
}
|
||||
|
||||
$factory = $this->collectionTypes[$type] ?? null;
|
||||
if ($factory) {
|
||||
return $factory->readStream($type, $filepath);
|
||||
}
|
||||
|
||||
throw new RuntimeException(sprintf('Reading media file failed: type %s does not exist', $type), 500);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ use Grav\Framework\File\Formatter\JsonFormatter;
|
||||
use Grav\Framework\File\JsonFile;
|
||||
use Grav\Framework\Image\Adapter\GdAdapter;
|
||||
use Grav\Framework\Image\Image;
|
||||
use Grav\Framework\Psr7\Response;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
|
||||
use RuntimeException;
|
||||
use function func_num_args;
|
||||
@@ -58,6 +60,70 @@ trait ImageMediaTrait
|
||||
/** @var string */
|
||||
protected $sizes = '100vw';
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @return array|null
|
||||
*/
|
||||
protected static function createImageFromCache(string $path): ?array
|
||||
{
|
||||
$filepath = GRAV_WEBROOT . $path;
|
||||
$cachepath = "{$filepath}.json";
|
||||
$file = static::getCacheMetaFile($cachepath);
|
||||
if (!$file->exists()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = $file->load();
|
||||
$format = $data['extra']['format'] ?? 'jpg';
|
||||
$mime = $data['extra']['mime'] ?? '';
|
||||
$quality = $data['extra']['quality'] ?? 80;
|
||||
$mediaUri = $data['extra']['media-uri'] ?? null;
|
||||
|
||||
$mediaFactory = Grav::instance()['media_factory'];
|
||||
|
||||
$fileData = $mediaFactory->readFile($mediaUri);
|
||||
$adapter = GdAdapter::createFromString($fileData);
|
||||
|
||||
$image = Image::createFromArray($data);
|
||||
|
||||
$image->setAdapter($adapter);
|
||||
$filepath = $image->save($filepath, $format, $quality);
|
||||
$image->freeAdapter();
|
||||
|
||||
$time = filemtime($filepath);
|
||||
$size = filesize($filepath);
|
||||
|
||||
return [$filepath, $mime, $time, $size];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $path
|
||||
* @return ResponseInterface
|
||||
*/
|
||||
public static function createImageResponseFromCache(string $path): ResponseInterface
|
||||
{
|
||||
[$filepath, $mime, $time, $size] = static::createImageFromCache($path);
|
||||
if (is_file($filepath)) {
|
||||
$code = 200;
|
||||
} else {
|
||||
$code = 404;
|
||||
// TODO: customizable 404 image?
|
||||
$filepath = GRAV_WEBROOT . '/system/images/media/thumb-jpg.png';
|
||||
$mime = 'image/png';
|
||||
$time = filemtime($filepath);
|
||||
$size = filesize($filepath);
|
||||
}
|
||||
|
||||
$body = fopen($filepath, 'rb');
|
||||
$headers = [
|
||||
'Content-Type' => $mime,
|
||||
'Last-Modified' => gmdate('D, d M Y H:i:s', $time) . ' GMT',
|
||||
'ETag' => sprintf('%x-%x', $size, $time)
|
||||
];
|
||||
|
||||
return new Response($code, $headers, $body);
|
||||
}
|
||||
|
||||
/**
|
||||
* Also unset the image on destruct.
|
||||
*/
|
||||
@@ -599,9 +665,12 @@ trait ImageMediaTrait
|
||||
$format = $extension;
|
||||
}
|
||||
|
||||
|
||||
$image = $this->image;
|
||||
$image->extra['format'] = $format;
|
||||
$image->extra['quality'] = $quality;
|
||||
$image->extra['media-uri'] = $this->getMedia()->getMediaUri($this->filename);
|
||||
$image->extra['mime'] = $this->mime;
|
||||
|
||||
$data = $image->jsonSerialize();
|
||||
$hash = $data['hash'];
|
||||
@@ -618,14 +687,15 @@ trait ImageMediaTrait
|
||||
$imageFile = '/' . $locator->getResource($imageFile, false);
|
||||
$cacheFile = $locator->getResource($cacheFile, true);
|
||||
|
||||
$file = $this->getCacheMetaFile($cacheFile);
|
||||
$file = static::getCacheMetaFile($cacheFile);
|
||||
if (!$file->exists()) {
|
||||
$file->save($data);
|
||||
} else {
|
||||
$file->touch();
|
||||
}
|
||||
|
||||
return $this->generateCacheImage(GRAV_WEBROOT . $imageFile);
|
||||
return GRAV_WEBROOT . $imageFile;
|
||||
//return $this->generateCacheImage(GRAV_WEBROOT . $imageFile);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -663,7 +733,7 @@ trait ImageMediaTrait
|
||||
* @param string $filepath
|
||||
* @return JsonFile
|
||||
*/
|
||||
protected function getCacheMetaFile(string $filepath): JsonFile
|
||||
protected static function getCacheMetaFile(string $filepath): JsonFile
|
||||
{
|
||||
$formatter = new JsonFormatter(['encode_options' => JSON_PRETTY_PRINT]);
|
||||
|
||||
|
||||
@@ -112,6 +112,18 @@ abstract class AbstractMedia implements ExportInterface, MediaCollectionInterfac
|
||||
*/
|
||||
abstract public function getUrl(string $filename): string;
|
||||
|
||||
/**
|
||||
* @param string $filename
|
||||
* @return string
|
||||
*/
|
||||
public function getMediaUri(string $filename): string
|
||||
{
|
||||
$schema = 'media-' . str_replace('_', '-', $this->getName());
|
||||
$path = $this->getRealPath($filename);
|
||||
|
||||
return "{$schema}://{$path}";
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
|
||||
@@ -13,7 +13,6 @@ use Grav\Common\Config\Config;
|
||||
use Grav\Common\Data\Blueprint;
|
||||
use Grav\Common\Media\Interfaces\ImageManipulateInterface;
|
||||
use Grav\Common\Media\Interfaces\ImageMediaInterface;
|
||||
use Grav\Common\Media\Interfaces\MediaCollectionInterface;
|
||||
use Grav\Common\Media\Interfaces\MediaLinkInterface;
|
||||
use Grav\Common\Media\Traits\ImageLoadingTrait;
|
||||
use Grav\Common\Media\Traits\ImageMediaTrait;
|
||||
|
||||
@@ -13,6 +13,7 @@ use Grav\Common\Config\Config;
|
||||
use Grav\Common\Debugger;
|
||||
use Grav\Common\Errors\Errors;
|
||||
use Grav\Common\Grav;
|
||||
use Grav\Common\Page\Medium\ImageMedium;
|
||||
use Grav\Common\Page\Pages;
|
||||
use Grav\Common\Plugins;
|
||||
use Grav\Common\Session;
|
||||
@@ -30,6 +31,7 @@ use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\ServerRequestInterface;
|
||||
use Psr\Http\Server\RequestHandlerInterface;
|
||||
use function defined;
|
||||
use function dirname;
|
||||
use function in_array;
|
||||
|
||||
/**
|
||||
@@ -98,6 +100,14 @@ class InitializeProcessor extends ProcessorBase
|
||||
// Load plugins.
|
||||
$this->initializePlugins();
|
||||
|
||||
// Image handling can return response right away.
|
||||
$response = $this->handleImageRequest($request);
|
||||
if ($response) {
|
||||
$this->stopTimer('_init');
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
// Load pages.
|
||||
$this->initializePages($config);
|
||||
|
||||
@@ -331,6 +341,31 @@ class InitializeProcessor extends ProcessorBase
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ServerRequestInterface $request
|
||||
* @return ResponseInterface|null
|
||||
*/
|
||||
protected function handleImageRequest(ServerRequestInterface $request): ?ResponseInterface
|
||||
{
|
||||
// Handle clockwork API calls.
|
||||
$uri = $request->getUri();
|
||||
$server = $request->getServerParams();
|
||||
$basePath = str_replace('\\', '/', dirname(parse_url($server['SCRIPT_NAME'], PHP_URL_PATH)));
|
||||
if ($basePath === '/') {
|
||||
$basePath = '';
|
||||
}
|
||||
$imagePath = $basePath . '/images/';
|
||||
$path = $uri->getPath();
|
||||
|
||||
if (str_starts_with($path, $imagePath)) {
|
||||
$path = mb_substr($path, mb_strlen($basePath));
|
||||
|
||||
return ImageMedium::createImageResponseFromCache($path);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Config $config
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user