1

我的图像存储在谷歌云存储中。它已经完成VichUploaderBundleKnpGaufretteBunlde取得了成功。以下是我如何配置它们:

服务

services:           
    app.google_cloud_storage.service:
        class: \Google_Service_Storage
        factory: [Minn\AdsBundle\Factory\GoogleCloudStorageServiceFactory, createService]

    minn.liip_imagine.cache.resolver.gcs:
        class: Minn\AdsBundle\Resolver\GoogleCloudStorageResolver 
        arguments:
            - @logger            
            - @app.google_cloud_storage.service
            - '%bucket%'
            - 'public-write'

knp_gaufrette 和 vich_uploader 配置

knp_gaufrette:
    stream_wrapper: ~
    adapters:
        motors_adapter:
            local:
                directory: %kernel.root_dir%/../web/files/motors
        gcsmotors_adapter:
            google_cloud_storage:
                service_id: 'app.google_cloud_storage.service'
                bucket_name: '%bucket%'
                options:
                    directory: motors

    filesystems:
        motors_fs:
            adapter:    motors_adapter
        gcsmotors_fs:
            adapter: gcsmotors_adapter

vich_uploader:
    db_driver: orm
    storage: gaufrette
    mappings:
        motors_file:
            uri_prefix:         motors
            upload_destination: gcsmotors_fs
            namer: vich_uploader.namer_uniqid 
            delete_on_remove: true   

现在,我需要通过LiipImagineBundle. 不幸的是,谷歌云存储不受LiipImagineBundle. 因此,我尝试以与此处指定的类似方式实现 Google Cloud Storage Resolver :自定义缓存解析器。以下是我配置解析器的方式:

minn.liip_imagine.cache.resolver.gcs:
    class: Minn\AdsBundle\Resolver\GoogleCloudStorageResolver
    arguments:
        - @logger            
        - @app.google_cloud_storage.service
        - '%bucket%'
        - 'public-write'
    tags:                                                                                                                                                                                 
         - { name: monolog.logger, channel: tester } 
         - { name: 'liip_imagine.cache.resolver', resolver: 'gcs' }

每当有 twig 调用图像时,解析器就会被实例化。它检查图像是否被存储(函数isStored($path)被调用并且记录器告诉你)!函数resolve($path, $filter)store(BinaryInterface $binary, $path, $filter) 永远不会被调用。奇怪的!我说这可能是因为数据加载器工作不正常。因此,我创建了一个新的数据加载器,其定义如下:

minn.liip_imagine.binary.loader.stream.gcsmotors_fs:
    class: Minn\AdsBundle\Loader\GCSStreamLoader
    arguments:
        - @logger
        - gaufrette://gcsmotors_fs/
    tags:
        - { name: monolog.logger, channel: tester } 
        - { name: 'liip_imagine.binary.loader', loader: 'stream.gcsmotors_fs' } 

你猜怎么着?自定义数据加载器永远不会被实例化!我错过了什么吗?

谢谢。


解析器和数据加载器

<?php

namespace Minn\AdsBundle\Resolver;

use \Google_Service_Storage;
use Liip\ImagineBundle\Imagine\Cache\Resolver\ResolverInterface;
use Liip\ImagineBundle\Binary\BinaryInterface;
use Liip\ImagineBundle\Exception\Imagine\Cache\Resolver\NotStorableException;
use Psr\Log\LoggerInterface;

/**
 * Description of GoogleCloudStorageResolver
 *
 * @author Mohamed Amine Jallouli <jallouli.med.amine@gmail.com>
 */
class GoogleCloudStorageResolver  implements ResolverInterface {

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

    /**
     * @var Google_Service_Storage
     */
    protected $service;

    /**
     * @var \Gaufrette\Adapter\GoogleCloudStorage
     */
    protected $adatpter;

    /**
     * @var \Gaufrette\Filesystem
     */
    protected $filesystem;

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

    /**
     * @var string
     */

    protected $acl;
    /**
     * @var array
     */
    protected $getOptions;

    /**
     * Object options added to PUT requests.
     *
     * @var array
     */
    protected $putOptions;

    /**
     * @var string
     */
    protected $cachePrefix= null;

    /**
     * @var string
     */
    protected $key= null;

    /**
     * Constructs a cache resolver storing images on Google Cloud Storage.
     *
     * @param LoggerInterface        $logger     monolog logger.
     * @param Google_Service_Storage $service    The Google Cloud Storage API. It's required to know authentication information.
     * @param string                 $bucket     The bucket name to operate on.
     * @param string                 $acl        The ACL
     * @param array                  $getOptions A list of options to be passed when retrieving the object url from Google Cloud Storage.
     * @param array                  $putOptions A list of options to be passed when saving the object to Google Cloud Storage.
     */
    public function __construct(
            LoggerInterface $logger,
            Google_Service_Storage $service,
            $bucket, 
            $acl,
            array $getOptions = array(),
            array $putOptions = array()
            ) {        
        $this->logger = $logger;
        $this->service = $service;
        $this->bucket = $bucket;
        $this->acl = $acl;
        $this->getOptions = $getOptions;
        $this->putOptions = $putOptions;

        $this->logger->info( "__construct: bucket=".$bucket);
    }

    /**
     * @param string $cachePrefix
     * 
     */
    protected function setCachePrefix($cachePrefix){
        $this->logger->info("cachePrefix= ".$cachePrefix);
        $this->cachePrefix = $cachePrefix;
    }

    /**
     * @param string $path
     */
    public function setKeyCachePrefix($path){
        $pos=  strpos($path, "/");
        $tmp=$path;
        while($pos!==false ){
            //$this->logger->notice("getKey [path=$path , pos=$pos" );
            $path = substr($path,$pos+1);
            $pos=  strpos($path, "/");
        }
        $this->logger->notice("getKey: key=".$path );
        $this->key = $path;

        $this->setCachePrefix(substr($tmp, 0, strpos($tmp, $path)-1));
        //$this->logger->notice("getKey: cachePrefix=". substr($tmp, 0, strpos($tmp, $path)-1));
    }


    /**
     * {@inheritDoc}
     */
    public function isStored($path, $filter){
        $this->logger->info("isStored? =  path=$path , filter=$filter");
        return $this->objectExists($path, $filter);
    }

    /**
     * {@inheritDoc}
     */
    public function resolve($path, $filter){
        $this->logger->info("resovle: path=$path , filter=$filter");
        return $this->getObjectUrl($path, $filter);
    }

    /**
     * {@inheritDoc}
     */
    public function store(BinaryInterface $binary, $path, $filter){
        $this->logger->notice("*-*-*-*-*-*-*-*- store() -*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*");
        $this->setKeyCachePrefix($path);

        // Creation of the $adatpter
         $this->adapter = new \Gaufrette\Adapter\GoogleCloudStorage(
                 $this->service,
                 $this->bucket, array(
                     'directory'=>$this->cachePrefix .'/'. $filter,
                     'acl'=>$this->acl), true);

         // Creation of the $filesystem
         $this->filesystem = new \Gaufrette\Filesystem($this->adapter);


         // I assume here that the file is already in the bucket. That's why, a BinaryInterface is given.
         // Consequently, Binary will be directly re-written in the bucket according to its filter.

         // let's log a little bit out store() function to know what is happening!
         $this->logger->info('store: key='.$this->key);
         $this->logger->info('store: filter='.$filter);
         $this->logger->info('store: binary='.$binary->getMimeType());

        try {
            $this->filesystem->write($this->key, $binary);
            $this->logger->info('store: filtered image='. $this->getObjectUrl($path, $filter));

        } catch (\Exception $e) {
            $this->logError('The object could not be created on Google Cloud Storage.', array(
                'objectPath'  => $this->getObjectPath($path, null),
                'filter'      => $filter,
                'bucket'      => $this->bucket,
                'exception'   => $e,
            ));
            throw new NotStorableException('The object could not be created on Google Cloud Storage.', null, $e);
        }
    }    

    /**
     * {@inheritDoc}
     */
    public function remove(array $paths, array $filters)
    {
        if (empty($paths) && empty($filters)) {
            return;
        }

        if (empty($paths)) {
            // I don't to do any thing for the moment!!
            // Not urgent for the moment!
            return;
        }

        foreach ($filters as $filter) {
            foreach ($paths as $path) {
                if (!$this->objectExists($path, $filter)) {
                    continue; 
                }

                try {
                    $this->adapter = new \Gaufrette\Adapter\GoogleCloudStorage(
                            $this->service,
                            $this->bucket, array(
                                'directory'=>$this->cachePrefix .'/'. $filter,
                                'acl'=>$this->acl), true);

                    // Creation of the $filesystem
                    $this->filesystem = new \Gaufrette\Filesystem($this->adapter);

                    // the file to be delete is here
                    $f = $this->getObjectUrl($path, $filter);

                    // deleting the file
                    $this->filesystem->delete($path);
                    $this->logger->info('delete(): DONE FOR '.$f);

                } catch (\Exception $e) {
                    $this->logError('The object could not be deleted from Google Cloud Storage.', array(
                        'path'        => $path,
                        'filter'      => $filter,
                        'bucket'      => $this->bucket,
                        'exception'   => $e,
                    ));
                }
            }
        }
    }    

    /**
     * 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 Google Cloud Storage.
     */
    public function getObjectPath($path, $filter){
        $this->setKeyCachePrefix($path);
        $path= $this->key;
        $path = $this->cachePrefix
            ? sprintf('%s/%s/%s', $this->cachePrefix, $filter, $path)
            : sprintf('%s/%s', $filter, $path);

        return str_replace('//', '/', $path);
    }

    /**
     * Returns the URL for an object saved on Google Cloud Storage.
     *
     * @param string $path
     * @param string $filter
     *
     * @return string
     */
    public function getObjectUrl($path, $filter){
        // AWS S3 solution (No need for it!)
        //return $this->storage->getObjectUrl($this->bucket, $path, 0, $this->getOptions);

        // Google Cloud Storage solution 
        return 'https://storage.googleapis.com/' .$this->bucket. '/' . $this->getObjectPath($path, $filter);
    }

    /**
     * Checks whether an object exists.
     *
     * @param string $objectPath
     *
     * @return bool
     */
    protected function objectExists($path, $filter){
        // AWS S3 solution... (No need for it!)
        //return $this->storage->doesObjectExist($this->bucket, $objectPath);

        // Google Cloud Storage solution
        $file = $this->getObjectUrl($path, $filter);
        $file_headers = @get_headers($file);
        $this->logger->info("objectExists: file=".$file);
        if($file_headers[0] == 'HTTP/1.0 200 OK' || $file_headers[0] == 'HTTP/1.1 200 OK') {
            $this->logger->info("objectExists: file_headers[0]=". $file_headers[0]);
            $exists = true;
        }
        else {
            $this->logger->info("objectExists: file_headers[0]=". $file_headers[0]);
            $exists = false;
        }
        return $exists;
    } 
    /**
     * @param mixed $message
     * @param array $context
     */
    protected function logError($message, array $context = array())
    {
        if ($this->logger) {
            $this->logger->error($message, $context);
        }
    }    
}

这是我的自定义 StreamLoader:

<?php

namespace Minn\AdsBundle\Loader;

//use Imagine\Image\ImagineInterface;
use Liip\ImagineBundle\Imagine\Data\Loader\LoaderInterface;
use Liip\ImagineBundle\Exception\Binary\Loader\NotLoadableException;

class GCSStreamLoader implements LoaderInterface{

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

    /*
     * @var ImagineInterface
     */
    //protected $imagine; 

    /**
     * The wrapper prefix to append to the path to be loaded.
     *
     * @var string
     */
    protected $wrapperPrefix;

    /**
     * A stream context resource to use.
     *
     * @var resource|null
     */
    protected $context;


    /**
     * @param LoggerInterface $logger
     * @param string          $wrapperPrefix
     * @param resource|null   $context
     *
     * @throws \InvalidArgumentException
     */
    public function __construct(LoggerInterface $logger/*, ImagineInterface $imagine*/, $wrapperPrefix, $context = null){

        $this->logger = $logger;
        /*$this->imagine= $imagine;*/

        $this->logger->notice("*************__construct (StreamLoader)*********************");
        $this->wrapperPrefix = $wrapperPrefix;
        $this->logger->notice("__construct: wrapperPrefix=$wrapperPrefix");
        $this->logger->notice("__construct: context=$context");
        if ($context && !is_resource($context)) {
            $this->logger->notice("__construct: InvalidArgumentException!!!");
            throw new \InvalidArgumentException('The given context is no valid resource.');
        }

        $this->context = $context;
    }

    /**
     * {@inheritDoc}
     */    
    public function find($path)
    {
        $name = $this->wrapperPrefix.$path;

        /*
         * This looks strange, but at least in PHP 5.3.8 it will raise an E_WARNING if the 4th parameter is null.
         * fopen() will be called only once with the correct arguments.
         *
         * The error suppression is solely to determine whether the file exists.
         * file_exists() is not used as not all wrappers support stat() to actually check for existing resources.
         */
        if (($this->context && !$resource = @fopen($name, 'r', null, $this->context)) || !$resource = @fopen($name, 'r')) {
            $this->logger->notice("find: NotLoadableException (3)!!! Source image %s not found".$name);
            throw new NotLoadableException(sprintf('Source image %s not found.', $name));
        }

        // Closing the opened stream to avoid locking of the resource to find.
        fclose($resource);

        try {
            $content = file_get_contents($name, null, $this->context);
        } catch (\Exception $e) {
            $this->logger->notice("find: NotLoadableException (2)!!! Source image %s could not be loaded". $name);
            throw new NotLoadableException(sprintf('Source image %s could not be loaded.', $name, $e));
        }

        if (false === $content) {
            $this->logger->notice("find: NotLoadableException (3)!!! Source image %s could not be loaded". $name);
            throw new NotLoadableException(sprintf('Source image %s could not be loaded.', $name));
        }

        return $content;
    }
}
4

0 回答 0