Skip to content

Commit

Permalink
Merge pull request #227 from havvg/master
Browse files Browse the repository at this point in the history
add AwsS3Resolver for new SDK version
  • Loading branch information
havvg committed Aug 21, 2013
2 parents 6234113 + 58da8aa commit ea3395e
Show file tree
Hide file tree
Showing 7 changed files with 602 additions and 0 deletions.
225 changes: 225 additions & 0 deletions Imagine/Cache/Resolver/AwsS3Resolver.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
<?php

namespace Liip\ImagineBundle\Imagine\Cache\Resolver;

use Aws\S3\Enum\CannedAcl;
use Aws\S3\S3Client;

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

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

use Symfony\Component\HttpKernel\Log\LoggerInterface;

class AwsS3Resolver implements ResolverInterface, CacheManagerAwareInterface
{
/**
* @var S3Client
*/
protected $storage;

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

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

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

/**
* @var array
*/
protected $objUrlOptions;

/**
* @var LoggerInterface
*/
protected $logger;

/**
* Constructs a cache resolver storing images on Amazon S3.
*
* @param S3Client $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
* @param array $objUrlOptions A list of options to be passed when retrieving the object url from Amazon S3.
*/
public function __construct(S3Client $storage, $bucket, $acl = CannedAcl::PUBLIC_READ, array $objUrlOptions = array())
{
$this->storage = $storage;

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

$this->objUrlOptions = $objUrlOptions;
}

/**
* Sets the logger to be used.
*
* @param LoggerInterface $logger
*/
public function setLogger(LoggerInterface $logger)
{
$this->logger = $logger;
}

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

/**
* {@inheritDoc}
*/
public function resolve(Request $request, $path, $filter)
{
$objectPath = $this->getObjectPath($path, $filter);
if ($this->objectExists($objectPath)) {
return new RedirectResponse($this->getObjectUrl($objectPath), 301);
}

return $objectPath;
}

/**
* {@inheritDoc}
*/
public function store(Response $response, $targetPath, $filter)
{
try {
$storageResponse = $this->storage->putObject(array(
'ACL' => $this->acl,
'Bucket' => $this->bucket,
'Key' => $targetPath,
'Body' => $response->getContent(),
));
} catch (\Exception $e) {
if ($this->logger) {
$this->logger->warn('The object could not be created on Amazon S3.', array(
'targetPath' => $targetPath,
'filter' => $filter,
));
}

return $response;
}

$response->setStatusCode(301);
$response->headers->set('Location', $storageResponse->get('ObjectURL'));

return $response;
}

/**
* {@inheritDoc}
*/
public function getBrowserPath($path, $filter, $absolute = false)
{
$objectPath = $this->getObjectPath($path, $filter);
if ($this->objectExists($objectPath)) {
return $this->getObjectUrl($objectPath);
}

return $this->cacheManager->generateUrl($path, $filter, $absolute);
}

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

try {
$response = $this->storage->deleteObject(array(
'Bucket' => $this->bucket,
'Key' => $targetPath,
));

return true;
} catch (\Exception $e) {
return false;
}
}

/**
* Sets a single option to be passed when retrieving an objects URL.
*
* If the option is already set, it will be overwritten.
*
* @see Aws\S3\S3Client::getObjectUrl() for available options.
*
* @param string $key The name of the option.
* @param mixed $value The value to be set.
*
* @return AmazonS3Resolver $this
*/
public function setObjectUrlOption($key, $value)
{
$this->objUrlOptions[$key] = $value;

return $this;
}

/**
* {@inheritDoc}
*/
public function clear($cachePrefix)
{
// TODO: implement cache clearing for Amazon S3 service
}

/**
* 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 str_replace('//', '/', $filter.'/'.$path);
}

/**
* Returns the URL for an object saved on Amazon S3.
*
* @param string $targetPath
*
* @return string
*/
protected function getObjectUrl($targetPath)
{
return $this->storage->getObjectUrl($this->bucket, $targetPath, 0, $this->objUrlOptions);
}

/**
* Checks whether an object exists.
*
* @param string $objectPath
*
* @return bool
*/
protected function objectExists($objectPath)
{
return $this->storage->doesObjectExist($this->bucket, $objectPath);
}
}
89 changes: 89 additions & 0 deletions Resources/doc/cache-resolver/aws_s3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
# AwsS3Resolver

The AwsS3Resolver requires the [aws-sdk-php](https://github.com/aws/aws-sdk-for-php).

You can add the SDK by adding those lines to your `deps` file.

``` ini
[aws-sdk]
git=git://github.com/aws/aws-sdk-php.git
```

Afterwards, you only need to configure some information regarding your AWS account and the bucket.

``` yaml
parameters:
amazon_s3.key: 'your-aws-key'
amazon_s3.secret: 'your-aws-secret'
amazon_s3.bucket: 'your-bucket.example.com'
amazon_s3.region: 'your-bucket-region'
```
Now you can set up the services required:
``` yaml
services:
acme.amazon_s3:
class: Aws\S3\S3Client
factory_class: Aws\S3\S3Client
factory_method: factory
arguments:
-
key: %amazon_s3.key%
secret: %amazon_s3.secret%
region: %amazon_s3.region%

acme.imagine.cache.resolver.amazon_s3:
class: Liip\ImagineBundle\Imagine\Cache\Resolver\AwsS3Resolver
arguments:
- "@acme.amazon_s3"
- "%amazon_s3.bucket%"
tags:
- { name: 'liip_imagine.cache.resolver', resolver: 'amazon_s3' }
```
Now you are ready to use the `AwsS3Resolver` by configuring the bundle.
The following example will configure the resolver is default.

``` yaml
liip_imagine:
cache: 'amazon_s3'
```

If you want to use other buckets for other images, simply alter the parameter names and create additional services!

## Object URL Options

In order to make use of the object URL options, you can simply add a call to the service, to alter those options you need.

``` yaml
services:
acme.imagine.cache.resolver.amazon_s3:
class: Liip\ImagineBundle\Imagine\Cache\Resolver\AwsS3Resolver
arguments:
- "@acme.amazon_s3"
- "%amazon_s3.bucket%"
calls:
# This calls $service->setObjectUrlOption('Scheme', 'https');
- [ setObjectUrlOption, [ 'Scheme', 'https' ] ]
tags:
- { name: 'liip_imagine.cache.resolver', resolver: 'amazon_s3' }
```

You can also use the constructor of the resolver to directly inject multiple options.

``` yaml
services:
acme.imagine.cache.resolver.amazon_s3:
class: Liip\ImagineBundle\Imagine\Cache\Resolver\AwsS3Resolver
arguments:
- "@acme.amazon_s3"
- "%amazon_s3.bucket%"
- "public-read" # Aws\S3\Enum\CannedAcl::PUBLIC_READ (default)
- { Scheme: https }
tags:
- { name: 'liip_imagine.cache.resolver', resolver: 'amazon_s3' }
```

- [Back to cache resolvers](../cache-resolvers.md)
- [Back to the index](../index.md)
1 change: 1 addition & 0 deletions Resources/doc/cache-resolvers.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Built-In CacheResolver

* [AmazonS3](cache-resolver/amazons3.md)
* [AwsS3](cache-resolver/aws_s3.md) - for SDK version 2
* [CacheResolver](cache-resolver/cache.md)

# Custom cache resolver
Expand Down
13 changes: 13 additions & 0 deletions Tests/Fixtures/CannedAcl.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Aws\S3\Enum;

class CannedAcl
{
const PRIVATE_ACCESS = 'private';
const PUBLIC_READ = 'public-read';
const PUBLIC_READ_WRITE = 'public-read-write';
const AUTHENTICATED_READ = 'authenticated-read';
const BUCKET_OWNER_READ = 'bucket-owner-read';
const BUCKET_OWNER_FULL_CONTROL = 'bucket-owner-full-control';
}
8 changes: 8 additions & 0 deletions Tests/Fixtures/Model.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

namespace Guzzle\Service\Resource;

class Model
{
public function get($key) { }
}
16 changes: 16 additions & 0 deletions Tests/Fixtures/S3Client.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Aws\S3;

class S3Client
{
public function doesBucketExist($bucket, $accept403, $options = array()) { }

public function doesObjectExist($bucket, $key, $options = array()) { }

public function putObject($args) { }

public function deleteObject($args) { }

public function getObjectUrl($bucket, $key, $expires = 0, $args = array()) { }
}
Loading

0 comments on commit ea3395e

Please sign in to comment.