imagecache
imagecache copied to clipboard
Cache in filesystem problems
This cache package seems very geared towards storing and returning the actual data stored for the image.
The problem is when it comes to filesystem, you really don't want to be reading image files into memory in PHP and then output the data somehow. Your whole framework shouldn't need to be booted up to serve an image. Ideally you want nginx to serve a cached version of the image stored on disk straight up.
With this package, it's extremely difficult. This might be related to issue #38.
It's good that it's general using the Flysystem, but I think there needs to be more consideration for those that don't want PHP to read the images but just have the cache return the stored image path.
Bump. Any thoughts @olivervogel ?
Interested as well; this package is so great, but it would be great with optimization on the cache.
This package is so awesome that I want to use it to create a dynamic image service. I also met this problem today and I have done some test on it. As below:
Solution
- Store the image file on the disk and make its path equals the route path.
- Config
try_filesin nginx.conf.
Version
laravel/framework: 5.2.* intervention/imagecache: ^2.3
Step
- Read the guideline first.
- Define a route in
app/Http/routes.php. Don't config the route inconfig/imagecache.php.
Route::get('imagecache/{template}/{filename}', [
'uses' => 'ImageCacheController@getResponse',
'as' => 'imagecache'
])->where('filename', '[ \w\\.\\/\\-\\@]+');
- Make a new controller named
App\Http\Controllers\ImageCacheControllerwhich extendsIntervention\Image\ImageCacheController. OverridegetImagemethod, and then copygetTemplate,getImagePathandbuildResponse, because they are private counld not be inherited.
namespace App\Http\Controllers;
use Illuminate\Http\Response;
use Intervention\Image\Facades\Image;
use Intervention\Image\ImageCacheController as BaseController;
class ImageCacheController extends BaseController
{
/**
* Get HTTP response of template applied image file
*
* @param string $template
* @param string $filename
* @return Illuminate\Http\Response
*/
public function getImage($template, $filename)
{
$filter = $this->getTemplate($template);
$path = $this->getImagePath($filename);
// It is different from the source code, just save it in the disk directly.
$cachePath = safe_mkdir(public_path("imagecache/$template/$filename"));
$image = Image::make($path)->filter($filter)->save($cachePath);
return $this->buildResponse($image);
}
/**
* Returns corresponding template object from given template name
*
* @param string $template
* @return mixed
*/
private function getTemplate($template)
{
$template = config("imagecache.templates.{$template}");
switch (true) {
// closure template found
case is_callable($template):
return $template;
// filter template found
case class_exists($template):
return new $template;
default:
// template not found
abort(404);
break;
}
}
/**
* Returns full image path from given filename
*
* @param string $filename
* @return string
*/
private function getImagePath($filename)
{
// find file
foreach (config('imagecache.paths') as $path) {
// don't allow '..' in filenames
$image_path = $path . '/' . str_replace('..', '', $filename);
if (file_exists($image_path) && is_file($image_path)) {
// file found
return $image_path;
}
}
// file not found
abort(404);
}
/**
* Builds HTTP response from given image data
*
* @param string $content
* @return Illuminate\Http\Response
*/
private function buildResponse($content)
{
// define mime type
$mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $content);
// return http response
return new Response($content, 200, [
'Content-Type' => $mime,
'Cache-Control' => 'max-age=' . (config('imagecache.lifetime') * 60) . ', public',
'Etag' => md5($content)
]);
}
}
- Add a helper function
safe_mkdir
use Symfony\Component\HttpFoundation\File\Exception\FileException;
if (! function_exists('safe_mkdir')) {
function safe_mkdir($directory, $mode = 0777)
{
$dir = pathinfo($directory, PATHINFO_EXTENSION) ? dirname($directory) : $directory;
if (! is_dir($dir)) {
if (false === mkdir($dir, $mode, true) && ! is_dir($dir)) {
throw new FileException(sprintf('Unable to create the "%s" directory', $dir));
}
} elseif (! is_writable($dir)) {
throw new FileException(sprintf('Unable to write in the "%s" directory', $dir));
}
return $directory;
}
}
- Config Nginx, modify
root,server_nameas yours.
server {
listen 80;
root path/to/laravel/public;
server_name local-laravel.com;
index index.php;
location / {
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
include fastcgi_params;
fastcgi_pass 127.0.0.1:9000;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
}
}
Test
- Reload or restart nginx
- Put an image
test.jpginpublic/upload/201605/10file - Access this link in browser http://local-laravel.com/imagecache/small/201605/10/test.jpg
- Comment or remove the code in
App\Http\Controllers\ImageCacheController::getImage - Force refresh the browser, you could still see the
smallimage although you have removed the code ofgetImage. Because thesmallimage has been already stored inpublic/imagecache/small/201605/10 - Delete the
smallimage file, and force refresh the browser again. And now you will lose it.
public function getImage($template, $filename) { }
Suggestion
- Does it support save dynamic images on disk in the package? I think we can make the methods
getTemplate,getImagePathandbuildResponseas public or protected at least. - Shall we add a
mkdirfeature inIntervention\Image\Image::save, likesafe_mkdir?
Finally, thanks for sharing @olivervogel . Maybe I'm not good at English, and could not explain something clearly. If anyone is confused, please let me know. ^_^