Laravel-Flysystem and Creating Time Limited Public Urls on S3

Posted: 2015-02-05 23:44:58

We needed to offer timed links to the urls and we are using Laravel-Flysystem

Aaron Smith found this how to but I wanted it to work with existing use of FlySystem.

I ended up moving the code into a trait so we could use this one feature

<?php namespace AlfredNutileInc\CoreApp\Helpers;
use GrahamCampbell\Flysystem\Facades\Flysystem;
use Illuminate\Support\Facades\Log;

trait S3Helper {

    protected $path_for_assets;
    protected $expire_time;
    public $driver = 'awss3';

    /**
     * @return string
     */
    public function getDriver()
    {
        return $this->driver;
    }

    /**
     * @param string $driver
     */
    public function setDriver($driver)
    {
        $this->driver = $driver;
    }

    public function iterateOverFiles($files)
    {
        foreach($files as $key => $file)
        {
            if(isset($file['path'])) {
                $files[$key]['url'] = $this->getSignedUrl($file['path']);
            }
        }
        return $files;
    }

    protected function getFiles()
    {
        $this->path_for_assets = $this->project_id . '/' . $this->id;
        $files = Flysystem::listContents($this->path_for_assets);
        if(!$files)
            return [];
        return $this->iterateOverFiles($files);
    }
    /**
     * https://coderwall.com/p/pr-gwg/create-aws-s3-signed-requests-with-php
     */
    protected function getSignedUrl($filename)
    {
        $awsKeyId = Flysystem::getConnectionConfig($this->getDriver())['key'];
        $awsSecret = Flysystem::getConnectionConfig($this->getDriver())['secret'];
        $expires = $this->getExpireTime();
        $httpVerb = "GET";
        $contentMD5 = "";
        $contentType = "";
        $amzHeaders = "";
        $amzResource = "/" . Flysystem::getConnectionConfig($this->getDriver())['bucket'] . "/" . $filename;
        $request = sprintf("%s\n%s\n%s\n%s\n%s%s" , $httpVerb , $contentMD5 , $contentType , $expires , $amzHeaders , $amzResource );
        $base64signed = urlencode(base64_encode( hash_hmac( 'sha1' , $request, $awsSecret , true ) ));
        $url = "http://s3.amazonaws.com%s?AWSAccessKeyId=%s&Expires=%s&Signature=%s";
        $url = sprintf( $url , $amzResource , $awsKeyId , $expires , $base64signed );
        return $url;
    }

    public function setExpireTime($time = false)
    {
        if($time == false)
        {
            $time = time() + (10*60);
        }

        $this->expire_time = $time;
    }

    public function getExpireTime()
    {
        if($this->expire_time == false)
        {
            $this->setExpireTime();
        }
        return $this->expire_time;
    }
}

Ideally seconds could be passed in too.

Then in any model/repo I can use it like this

    public function assets()
    {
        try
        {
            $files = $this->getFiles();
            return ['images' => $files, 'videos' => []];
        } catch(\Exception $e)
        {
            throw new \Exception(sprintf("Error getting files %s", $e->getMessage()));
        }
    }

Tags:

laravel php