Skip to content

Commit

Permalink
Merge pull request #1 from thomaswelton/upload-dir
Browse files Browse the repository at this point in the history
Upload dir
  • Loading branch information
thomaswelton committed Oct 1, 2013
2 parents cd3c90c + 12f6956 commit a543e84
Show file tree
Hide file tree
Showing 7 changed files with 239 additions and 6 deletions.
27 changes: 26 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
[![Build Status](https://travis-ci.org/thomaswelton/laravel-rackspace-opencloud.png?branch=master)](https://travis-ci.org/thomaswelton/laravel-rackspace-opencloud)
[![Latest Stable Version](https://poser.pugx.org/thomaswelton/laravel-rackspace-opencloud/v/stable.png)](https://packagist.org/packages/thomaswelton/laravel-rackspace-opencloud)
[![Total Downloads](https://poser.pugx.org/thomaswelton/laravel-rackspace-opencloud/downloads.png)](https://packagist.org/packages/thomaswelton/laravel-rackspace-opencloud)
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/thomaswelton/laravel-rackspace-opencloud/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
Expand Down Expand Up @@ -36,6 +35,32 @@ Edit the config file to include your username, api key and region.

# Usage

## Artisan Commands

Upload files via the command line.

Synchronize a whole directory. Copies all files to /public/assets
```
php artisan cdn:sync public/assets
```

Copies all files to /assets trimming 'public' from the path
```
php artisan cdn:sync public/assets --trim=public
```

The sync command will save a file adjacent to the synchronized directory. It contains the http and https urls for your container. Along with a md5 hash of the directory.
In this way when a file changes inside a directory and is reuploaded you get a new cache busted URL.

If you are using the URL helper then it will return a CDN url for a file, if it finds a *.cdn.json file adjacent to one of it's parent directories.

```
URL::asset('assets/image.jpg');
```

You should be able to run `php artisan cdn:sync public/assets --trim=public` before or during a deployment and once complete all files being called by `URL::asset()` will return a CDN resource


## Upload to CDN

```php
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"php": ">=5.3.0"
, "illuminate/support": "4.0.x"
, "rackspace/php-opencloud": "dev-master"
, "alchemy/zippy": "0.1.0"
}
, "autoload": {
"classmap": [
Expand Down
110 changes: 110 additions & 0 deletions src/Thomaswelton/LaravelRackspaceOpencloud/Commands/CdnSyncCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
<?php namespace Thomaswelton\LaravelRackspaceOpencloud\Commands;

use \File;
use \Str;

use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;

class CdnSyncCommand extends Command {

/**
* The console command name.
*
* @var string
*/
protected $name = 'cdn:sync';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Upload a file or directory to a CDN';

/**
* Create a new command instance.
*
* @return void
*/
public function __construct()
{
parent::__construct();
}

/**
* Execute the console command.
*
* @return void
*/
public function fire()
{
$opencloud = \App::make('open-cloud');
$container_name = \Config::get('laravel-rackspace-opencloud::container');
$container = $opencloud->getContainer($container_name);

// Get directory or file path
$path = base_path() . '/' . $this->argument('path');
$path_trim = base_path() . '/' . $this->option('trim');

$this->info('Syncing to CDN: ' . $path);

// Exit if not exists
if(!File::isDirectory($path)){
return $this->error('Path is not a directory');
}

$files = File::allFiles($path);

// Get an md5 of a concatenated md5_file hash of all files
$directoryHash = md5(array_reduce($files, function($hash, $file){
// Do not include .cdn.json files in the directory hash
if(substr($file, -9) == '.cdn.json'){
return $hash;
}

$hash .= md5_file($file);
return $hash;
}));

$fileCount = count($files);
$this->info('Found ' . $fileCount . ' ' . Str::plural('file', $fileCount));

$cdnFile = $opencloud->uploadDir($container_name, $path, $directoryHash, $path_trim);

$cdnJsonArray = array(
'http' => $container->PublicURL(),
'https' => $container->SSLURI(),
'prefix' => $directoryHash,
'created' => time()
);

File::put($path . '.cdn.json', json_encode($cdnJsonArray));
}

/**
* Get the console command arguments.
*
* @return array
*/
protected function getArguments()
{
return array(
array('path', InputArgument::REQUIRED, 'File or directory path'),
);
}

/**
* Get the console command options.
*
* @return array
*/
protected function getOptions()
{
return array(
array('trim', '', InputOption::VALUE_OPTIONAL, 'String to trim from directory when uploading', null),
);
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<?php namespace Thomaswelton\LaravelRackspaceOpencloud;

use Illuminate\Support\ServiceProvider;
use Thomaswelton\LaravelRackspaceOpencloud\Commands;

class LaravelRackspaceOpencloudServiceProvider extends ServiceProvider {

Expand Down Expand Up @@ -32,6 +33,19 @@ public function register()
{
return new OpenCloud;
});

$this->app['cdn.sync'] = $this->app->share(function($app)
{
return new Commands\CdnSyncCommand;
});

$this->app->bind('url', function()
{
$routes = $this->app['router']->getRoutes();
return new UrlGenerator($routes, $this->app['request']);
});

$this->commands('cdn.sync');
}

/**
Expand Down
49 changes: 45 additions & 4 deletions src/Thomaswelton/LaravelRackspaceOpencloud/OpenCloud.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

use \Config;
use \File;
use Alchemy\Zippy\Zippy;

// 5 minutes
define('RAXSDK_TIMEOUT', 300);

class OpenCloud extends \OpenCloud\Rackspace{

Expand All @@ -27,8 +31,9 @@ public function getContainer($name){
$container = $this->getObjectStore()->Container();
$container->Create(array('name' => $name ));

// publish it to the CDN
$container->PublishToCDN();
// publish it to the CDN with 1 year TTL
$ttl = 60 * 60 * 24 * 365;
$container->PublishToCDN($ttl);

return $container;
}
Expand All @@ -53,16 +58,52 @@ public function upload($container, $file, $name = null)
}
}

public function createDataObject($container, $filePath, $fileName = null)
// Create and archive and upload a whole directory
// $dir - Directory to upload
// $cdnDir - Directory on the CDN to upload to
// $dirTrim - Path segments to trim from the dir path when on the CDN
public function uploadDir($container, $dir, $cdnDir = '', $dirTrim = ''){
$temp_file = storage_path() . '/CDN-' . time() . '.tar.gz';

$zip_dir_name = (0 === strpos($dir, $dirTrim)) ? substr($dir, strlen($dirTrim) + 1) : $dir;

$zippy = Zippy::load();
// creates an archive.zip that contains a directory "folder" that contains
// files contained in "/path/to/directory" recursively
$archive = $zippy->create($temp_file, array(
$cdnDir . '/' . $zip_dir_name => $dir
), true);

$cdnFile = $this->createDataObject($container, $temp_file, '/', 'tar.gz');

File::delete($temp_file);

return $cdnFile;
}

public function exisits($container, $file){
$container = $this->getContainer($container);
try{
return $container->DataObject($file);
}catch(\OpenCloud\Common\Exceptions\ObjFetchError $e){
return false;
}
}

public function createDataObject($container, $filePath, $fileName = null, $extract = null)
{
if(is_null($fileName)){
$fileName = basename($filePath);
}

$container = $this->getContainer($container);

$headers = array(
"Access-Control-Allow-Origin" => "*"
);

$object = $container->DataObject();
$object->Create(array('name'=> $fileName), $filePath);
$object->Create(array('name'=> $fileName, 'extra_headers' => $headers), $filePath, $extract);

return $object;
}
Expand Down
41 changes: 41 additions & 0 deletions src/Thomaswelton/LaravelRackspaceOpencloud/UrlGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php namespace Thomaswelton\LaravelRackspaceOpencloud;

use \File;
use \Request;
use Illuminate\Routing\UrlGenerator as LaravelGenerator;

class UrlGenerator extends LaravelGenerator{

/**
* Generate a URL to an application asset.
*
* @param string $path
* @param bool $secure
* @return string
*/
public function asset($path, $secure = null)
{
// Start looking for a CDN json file
$checkDir = dirname(public_path() . '/' . $path);

// Look up through the directories looking for a
// CDN json file
while($checkDir !== public_path()){
$cdnJsonPath = $checkDir . '.cdn.json';

if(File::isFile($cdnJsonPath)){
$json = File::get($cdnJsonPath);
$cdnObject = json_decode($json);

$baseUrl = ($secure || Request::secure()) ? $cdnObject->https : $cdnObject->http;

return $baseUrl . '/'. $cdnObject->prefix . '/' . $path;
}

$checkDir = dirname($checkDir);
}

return parent::asset($path, $secure);
}

}
3 changes: 2 additions & 1 deletion src/config/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
return array(
'region' => 'RACKSPACE_REGION',
'username' => 'RACKSPACE_USERNAME',
'apiKey' => 'RACKSPACE_API_KEY'
'apiKey' => 'RACKSPACE_API_KEY',
'container' => 'DEFUALT_CONTAINER_NAME'
);

0 comments on commit a543e84

Please sign in to comment.