Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add AmazonS3Resolver and ResolverInterface::remove #66

Merged
merged 2 commits into from
May 4, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 18 additions & 9 deletions Imagine/Cache/CacheManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ class CacheManager
private $resolvers = array();

/**
* Constructs the cache manager to handle Resolvers based on the provided FilterConfiguration.
*
* @param FilterConfiguration $filterConfig
* @param Filesystem $filesystem
* @param RouterInterface $router
* @param string $webRoot
* @param string $defaultResolver
*/
Expand All @@ -54,7 +56,7 @@ public function __construct(FilterConfiguration $filterConfig, RouterInterface $
/**
* @param string $filter
* @param ResolverInterface $resolver
*
*
* @return void
*/
public function addResolver($filter, ResolverInterface $resolver)
Expand Down Expand Up @@ -113,13 +115,7 @@ private function getResolver($filter)
*/
public function getBrowserPath($targetPath, $filter, $absolute = false)
{
$params = array('path' => ltrim($targetPath, '/'));

return str_replace(
urlencode($params['path']),
urldecode($params['path']),
$this->router->generate('_imagine_'.$filter, $params, $absolute)
);
return $this->getResolver($filter)->getBrowserPath($targetPath, $filter, $absolute);
}

/**
Expand Down Expand Up @@ -163,4 +159,17 @@ public function store(Response $response, $targetPath, $filter)

return $response;
}

/**
* Remove a cached image from the storage.
*
* @param string $targetPath
* @param string $filter
*
* @return bool
*/
public function remove($targetPath, $filter)
{
return $this->getResolver($filter)->remove($targetPath, $filter);
}
}
7 changes: 5 additions & 2 deletions Imagine/Cache/Resolver/AbstractFilesystemResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,20 @@ abstract class AbstractFilesystemResolver implements ResolverInterface
protected $filesystem;

/**
* Constructs cache web path resolver
* Constructs a filesystem based cache resolver.
*
* @param Filesystem $filesystem
* @param Filesystem $filesystem
*/
public function __construct(Filesystem $filesystem)
{
$this->filesystem = $filesystem;
}

/**
* Stores the content into a static file.
*
* @throws \RuntimeException
*
* @param Response $response
* @param string $targetPath
* @param string $filter
Expand Down
135 changes: 135 additions & 0 deletions Imagine/Cache/Resolver/AmazonS3Resolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
<?php

namespace Liip\ImagineBundle\Imagine\Cache\Resolver;

use \AmazonS3;

use Liip\ImagineBundle\Imagine\Cache\CacheManagerAwareInterface,
Liip\ImagineBundle\Imagine\Cache\CacheManager;

use Symfony\Component\HttpFoundation\Request,
Symfony\Component\HttpFoundation\RedirectResponse,
Symfony\Component\HttpFoundation\Response;

class AmazonS3Resolver implements ResolverInterface, CacheManagerAwareInterface
{
/**
* @var AmazonS3
*/
protected $storage;

/**
* @var string
*/
protected $bucket;

/**
* @var string
*/
protected $acl;

/**
* @var CacheManager
*/
protected $cacheManager;

/**
* Constructs a cache resolver storing images on Amazon S3.
*
* @throws \S3_Exception While checking for existence of the bucket.
*
* @param \AmazonS3 $storage The Amazon S3 storage API. It's required to know authentication information.
* @param string $bucket The bucket name to operate on.
* @param string $acl The ACL to use when storing new objects. Default: owner read/write, public read
*/
public function __construct(AmazonS3 $storage, $bucket, $acl = AmazonS3::ACL_PUBLIC)
{
$this->storage = $storage;
$this->storage->if_bucket_exists($bucket);

$this->bucket = $bucket;
$this->acl = $acl;
}

/**
* @param CacheManager $cacheManager
*/
public function setCacheManager(CacheManager $cacheManager)
{
$this->cacheManager = $cacheManager;
}

/**
* {@inheritDoc}
*/
public function resolve(Request $request, $path, $filter)
{
return $this->getObjectPath($path, $filter);
}

/**
* {@inheritDoc}
*/
public function store(Response $response, $targetPath, $filter)
{
$storageResponse = $this->storage->create_object($this->bucket, $targetPath, array(
'body' => $response->getContent(),
'contentType' => $response->headers->get('Content-Type'),
'length' => strlen($response->getContent()),
'acl' => $this->acl,
));

if ($storageResponse->isOK()) {
$response->setStatusCode(301);
$response->headers->set('Location', $this->storage->get_object_url($this->bucket, $targetPath));
}

return $response;
}

/**
* {@inheritDoc}
*/
public function getBrowserPath($targetPath, $filter, $absolute = false)
{
$objectPath = $this->getObjectPath($targetPath, $filter);
if ($this->storage->if_object_exists($this->bucket, $objectPath)) {
return $this->storage->get_object_url($this->bucket, $objectPath);
}

$params = array('path' => ltrim($targetPath, '/'));

return str_replace(
urlencode($params['path']),
urldecode($params['path']),
$this->cacheManager->getRouter()->generate('_imagine_'.$filter, $params, $absolute)
);
}

/**
* {@inheritDoc}
*/
public function remove($targetPath, $filter)
{
$objectPath = $this->getObjectPath($targetPath, $filter);
if (!$this->storage->if_object_exists($this->bucket, $objectPath)) {
// A non-existing object to delete: done!
return true;
}

return $this->storage->delete_object($this->bucket, $objectPath)->isOK();
}

/**
* Returns the object path within the bucket.
*
* @param string $path The base path of the resource.
* @param string $filter The name of the imagine filter in effect.
*
* @return string The path of the object on S3.
*/
protected function getObjectPath($path, $filter)
{
return $filter.'/'.$path;
}
}
45 changes: 36 additions & 9 deletions Imagine/Cache/Resolver/ResolverInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,49 @@
interface ResolverInterface
{
/**
* Resolves filtered path for rendering in the browser
* Resolves filtered path for rendering in the browser.
*
* @param Request $request
* @param string $path
* @param string $filter
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException In case the path can not be resolved.
*
* @return string target path
* @param Request $request The request made against a _imagine_* filter route.
* @param string $path The path where the resolved file is expected.
* @param string $filter The name of the imagine filter in effect.
*
* @return string|Response The target path to be used in other methods of this Resolver,
* a Response may be returned to avoid calling store upon resolution.
*/
function resolve(Request $request, $path, $filter);

/**
* @param Response $response
* @param string $targetPath
* @param string $filter
* Stores the content of the given Response.
*
* @param Response $response The response provided by the _imagine_* filter route.
* @param string $targetPath The target path provided by the resolve method.
* @param string $filter The name of the imagine filter in effect.
*
* @return Response
* @return Response The (modified) response to be sent to the browser.
*/
function store(Response $response, $targetPath, $filter);

/**
* Returns a web accessible URL.
*
* @param string $targetPath The target path provided by the resolve method.
* @param string $filter The name of the imagine filter in effect.
* @param bool $absolute Whether to generate an absolute URL or a relative path is accepted.
* In case the resolver does not support relative paths, it may ignore this flag.
*
* @return string
*/
function getBrowserPath($targetPath, $filter, $absolute = false);

/**
* Removes a stored image resource.
*
* @param string $targetPath The target path provided by the resolve method.
* @param string $filter The name of the imagine filter in effect.
*
* @return bool Whether the file has been removed successfully.
*/
function remove($targetPath, $filter);
}
95 changes: 71 additions & 24 deletions Imagine/Cache/Resolver/WebPathResolver.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@

namespace Liip\ImagineBundle\Imagine\Cache\Resolver;

use Liip\ImagineBundle\Imagine\Cache\CacheManagerAwareInterface,
Liip\ImagineBundle\Imagine\Cache\CacheManager;

use Symfony\Component\HttpFoundation\Request,
Symfony\Component\HttpFoundation\RedirectResponse,
Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

use Liip\ImagineBundle\Imagine\Cache\CacheManagerAwareInterface,
Liip\ImagineBundle\Imagine\Cache\CacheManager;

class WebPathResolver extends AbstractFilesystemResolver implements CacheManagerAwareInterface
{
/**
* @var CacheManager;
* @var CacheManager
*/
protected $cacheManager;

Expand All @@ -25,37 +25,84 @@ public function setCacheManager(CacheManager $cacheManager)
}

/**
* Resolves filtered path for rendering in the browser
* {@inheritDoc}
*/
public function resolve(Request $request, $path, $filter)
{
$browserPath = $this->decodeBrowserPath($this->getBrowserPath($path, $filter));
$targetPath = $this->getFilePath($path, $filter, $request->getBaseUrl());

// if the file has already been cached, we're probably not rewriting
// correctly, hence make a 301 to proper location, so browser remembers
if (file_exists($targetPath)) {
return new RedirectResponse($request->getBasePath().$browserPath);
}

return $targetPath;
}

/**
* {@inheritDoc}
*/
public function getBrowserPath($targetPath, $filter, $absolute = false)
{
$params = array('path' => ltrim($targetPath, '/'));

return str_replace(
urlencode($params['path']),
urldecode($params['path']),
$this->cacheManager->getRouter()->generate('_imagine_'.$filter, $params, $absolute)
);
}

/**
* {@inheritDoc}
*/
public function remove($targetPath, $filter)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can't this method be moved to AbstractFilesystemResolver ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it can; will update it.

{
$filename = $this->getFilePath($targetPath, $filter);
$this->filesystem->remove($filename);

return file_exists($filename);
}

/**
* Return the local filepath.
*
* @throws \Symfony\Component\HttpKernel\Exception\NotFoundHttpException
*
* @param Request $request
* @param string $path
* @param string $filter
* @param string $path The resource path to convert.
* @param string $filter The name of the imagine filter.
* @param string $basePath An optional base path to remove from the path.
*
* @return string target path
* @return string
*/
public function resolve(Request $request, $path, $filter)
protected function getFilePath($path, $filter, $basePath = '')
{
//TODO: find out why I need double urldecode to get a valid path
$browserPath = urldecode(urldecode($this->cacheManager->getBrowserPath($path, $filter)));
$browserPath = $this->decodeBrowserPath($this->getBrowserPath($path, $filter));

// if cache path cannot be determined, return 404
// if cache path cannot be determined, return 404
if (null === $browserPath) {
throw new NotFoundHttpException('Image doesn\'t exist');
}

$basePath = $request->getBaseUrl();
if (!empty($basePath) && 0 === strpos($browserPath, $basePath)) {
$browserPath = substr($browserPath, strlen($basePath));
if (!empty($basePath) && 0 === strpos($browserPath, $basePath)) {
$browserPath = substr($browserPath, strlen($basePath));
}

$targetPath = $this->cacheManager->getWebRoot().$browserPath;

// if the file has already been cached, we're probably not rewriting
// correctly, hence make a 301 to proper location, so browser remembers
if (file_exists($targetPath)) {
return new RedirectResponse($request->getBasePath().$browserPath);
}
return $this->cacheManager->getWebRoot().$browserPath;
}

return $targetPath;
/**
* Decodes the URL encoded browser path.
*
* @param string $browserPath
*
* @return string
*/
protected function decodeBrowserPath($browserPath)
{
//TODO: find out why I need double urldecode to get a valid path
return urldecode(urldecode($browserPath));
}
}
Loading