Save Files To Clouds

Last updated: October 11th, 2019

Save Files To Clouds

To a Cloud using Guzzle

First you need to install Guzzle via composer:

composer require guzzlehttp/guzzle:~6.0

After installing the package, you shoud create a class that is implemented by CloudInterface:

use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Streaming\Clouds\CloudInterface;

class MyCloud implements CloudInterface
{
    private $client;
    /**
     * @var  string
     */
    private $url;
    /**
     * @var  string
     */
    private $method;
    /**
     * @var  array
     */
    private $options;

    /**
     * @param  string $url
     * @param  string $method
     * @param  array $options
     */
    public function __construct(string $url, string $method = "GET", $options = [])
    {
        $this->client = new Client();
        $this->url = $url;
        $this->method = $method;
        $this->options = $options;
    }

    /**
     * @param  string $dir
     * @param  array $options
     */
    public function uploadDirectory(string $dir, array $options): void
    {
        $multipart = [];

        $name = $options['name'];
        $headers = isset($options['headers']) ? $options['headers'] : [];

        foreach (scandir($dir) as $key => $filename) {
            $path = $dir . DIRECTORY_SEPARATOR . $filename;

            if (is_file($path)) {
                $multipart[$key]['name'] = $name;
                $multipart[$key]['contents'] = fopen($path, 'r');
                if (!empty($headers)) {
                    $multipart[$key]['headers'] = $headers;
                }

                $multipart[$key]['filename'] = $filename;
            }
        }

        $this->sendRequest(array_merge($this->options, ['multipart' => array_values($multipart)]));
    }

    /**
     * @param  string $save_to
     * @param  array $options
     */
    public function download(string $save_to, array $options = []): void
    {
        $this->sendRequest(array_merge($this->options, ['sink' => $save_to]));
    }

    /**
     * @param  array $options
     * @throws  RuntimeException
     */
    private function sendRequest(array $options): void
    {
        try {
            $this->client->request($this->method, $this->url, $options);
        } catch (GuzzleException $e) {
            $error = sprintf('The url("%s") is not downloadable:\n' . "\n\nExit Code: %s(%s)\n\nbody:\n: %s",
                $this->url,
                $e->getCode(),
                $e->getMessage(),
                (method_exists($e->getResponse(), 'getBody')) ? $e->getResponse()->getBody()->getContents() : ""
            );

            throw new RuntimeException($error, $e->getCode(), $e);
        }
    }
}

After that, pass a associative array to the "save" method:

$api = 'https://www.aminyazdanpanah.com/api/v1.0/video/uploading';
$method = 'POST';
$current_percentage = 0;
$options = [
    'auth' => ['username', 'password', 'digest'],
    'progress' => function(
        $downloadTotal,
        $downloadedBytes,
        $uploadTotal,
        $uploadedBytes
    ) use (&$current_percentage) {
        $percentage = ($uploadTotal > 500) ? intval(($uploadedBytes / $uploadTotal) * 100) : 0;
        if ($current_percentage !== $percentage) {
            // You can update a field in your database or log it into a file
            // You can also create a socket connection and show the progress to users
            echo sprintf("\r Uploading... (%s%%)[%s%s]", $percentage, str_repeat('#', $percentage), str_repeat('-', (100 - $percentage)));
            $current_percentage = $percentage;
        }
    },
];

$cloud = new MyCloud($api, $method, $options);
$cloud_upload_options = [
    'name' => MY_FILES_name
    'headers' => [
        'User-Agent'        => 'Mozilla/5.0 (compatible; AminYazdanpanahBot/1.0; +http://aminyazdanpanah.com/bots)',
        'Accept'            => 'application/json',
        'Authorization'     => 'Bearer ACCESS_TOKEN'
    ]
];

$to_cloud = [
    'cloud' => $cloud,
    'options' => $cloud_upload_options
];

$dash->save(null, $to_cloud);

A path can also be passed to save a copy of files on your local machine:

$hls->save('/var/www/media/videos/hls/test.m3u8', $to_cloud);

To Amazon S3

You can save and upload entire packaged video files to Amazon S3. For uploading files, you need to have credentials.

First you need to install AWS SDK via composer:

composer require aws/aws-sdk-php

After installing the package, you shoud create a class that is implemented by CloudInterface:

use Aws\S3\Exception\S3Exception;
use Aws\S3\S3Client;
use Aws\S3\Transfer;
use Streaming\Clouds\CloudInterface;

class AWS implements CloudInterface
{
    private $s3;

    /**
     * AWS constructor.
     * @param  $config
     */
    public function __construct(array $config)
    {
        $this->s3 = new S3Client($config);;
    }

    /**
     * @param  string $dir
     * @param  array $options
     */
    public function uploadDirectory(string $dir, array $options): void
    {
        $dest = $options['dest'];

        try {
            $manager = new Transfer($this->s3, $dir, $dest);
            $manager->transfer();
        } catch (S3Exception $e) {
            throw new RuntimeException("There was an error downloading the file.\n error: " . $e->getMessage(), $e->getCode(), $e);
        }
    }

    /**
     * @param  string $save_to
     * @param  array $options
     * @throws  Exception
     */
    public function download(string $save_to, array $options): void
    {
        try {
            $file = $this->s3->getObject($options);

            if ($file['ContentLength'] > 0 && !empty($file['ContentType'])) {
                $body = $file->get('Body');
                file_put_contents($save_to, $body);
            } else {
                throw new Exception("There is no file in the bucket");
            }
        } catch (S3Exception $e) {
            throw new RuntimeException("There was an error downloading the file.\n error: " . $e->getMessage(), $e->getCode(), $e);
        }
    }
}

After that, pass a associative array to the "save" method:

$config = [
    'version'     => 'latest',
    'region'      => 'us-west-1',
    'credentials' => [
        'key'    => 'my-access-key-id',
        'secret' => 'my-secret-access-key',
    ]
];

$aws_cloud = new AWS($config);
$aws_cloud_upload_options = [
    'dest' => 's3://bucket'
];

$to_aws_cloud = [
    'cloud' => $aws_cloud,
    'options' => $aws_cloud_upload_options
];

$dash->save(null, $to_aws_cloud);

A path can also be passed to save a copy of files on your local machine.

$hls->save('/var/www/media/videos/hls/test.m3u8', $to_aws_cloud);

NOTE:

For more information, please read AWS SDK for PHP document.

TO Google Cloud Storage

You can save and upload entire packaged video files to Google Cloud Storage. For uploading files, you need to have credentials.

First you need to install Google Cloud Storage via composer:

composer require google/cloud-storage

After installing the package, you shoud create a class that is implemented by CloudInterface:

use Google\Cloud\Storage\StorageClient;
use Streaming\Clouds\CloudInterface;

class GoogleCloudStorage implements CloudInterface
{
    /**
     * @var  \Google\Cloud\Storage\Bucket $bucket
     */
    private $bucket;

    /**
     * GoogleCloudStorage constructor.
     * @param  string $bucket
     * @param  bool $userProject
     * @param  array $config
     */
    public function __construct(array $config, string $bucket, $userProject = false)
    {
        try {
            $storage = new StorageClient($config);
            $this->bucket = $storage->bucket($bucket, $userProject);
        } catch (\Exception $e) {
            throw new InvalidArgumentException(sprintf("Invalid inputs:\n %s", $e->getMessage()), $e->getCode(), $e);
        }
    }

    /**
     * @param  string $dir
     * @param  array $options
     */
    public function uploadDirectory(string $dir, array $options = []): void
    {
        try {
            foreach (scandir($dir) as $filename) {
                $path = $dir . DIRECTORY_SEPARATOR . $filename;

                if (is_file($path)) {
                    $this->bucket->upload(fopen($path, 'r'), $options);
                }
            }
        } catch (\Exception $e) {
            throw new RuntimeException(sprintf("There was an error during uploading files:\n %s", $e->getMessage()), $e->getCode(), $e);
        }
    }

    /**
     * @param  string $save_to
     * @param  array $options
     */
    public function download(string $save_to, array $options): void
    {
        $name = $options['filename'];
        unset($options['filename']);

        try {
            $this->bucket->object($name, $options)
                ->downloadToFile($save_to);
        } catch (\Exception $e) {
            throw new RuntimeException(sprintf("There was an error during fetch the file:\n %s", $e->getMessage()), $e->getCode(), $e);
        }
    }
}

After that, pass a associative array to the "save" method:

$config = [
    'keyFilePath' => "/path/to/credentials.json"
];
$bucket = 'my_bucket';

$google_cloud = new GoogleCloudStorage($config, $bucket);
// You can add options to the upload/download method(e.g. encryption) or it can be null
$google_cloud_upload_options = [
    'encryptionKey' => base64EncryptionKey,
    OTHER_OPTION => OTHER_VALUE_OPTIONS
];

$to_google_cloud= [
    'cloud' => $google_cloud,
    'options' => $google_cloud_upload_options
];

$dash->save(null, $to_google_cloud);

A path can also be passed to save a copy of files on your local machine.

$hls->save('/var/www/media/videos/hls/test.m3u8', $to_google_cloud);

TO Microsoft Azure Storage

You can save and upload the entire files to Microsoft Azure Storage. For uploading files, you need to have credentials.

First you need to install Microsoft Azure Storage via composer:

composer require microsoft/azure-storage-blob

After installing the package, you shoud create a class that is implemented by CloudInterface:

use MicrosoftAzure\Storage\Blob\BlobRestProxy;
use MicrosoftAzure\Storage\Common\Exceptions\ServiceException;
use Streaming\Clouds\CloudInterface;

class MicrosoftAzure implements CloudInterface
{
    private $blobClient;

    /**
     * MicrosoftAzure constructor.
     * @param  $connectionString
     */
    public function __construct($connectionString)
    {
        $this->blobClient = BlobRestProxy::createBlobService($connectionString);
    }

    /**
     * Upload a entire directory to a cloud
     * @param  string $dir
     * @param  array $options
     */
    public function uploadDirectory(string $dir, array $options): void
    {
        $container = $options['container'];

        try {
            foreach (scandir($dir) as $filename) {
                $path = $dir . DIRECTORY_SEPARATOR . $filename;

                if (is_file($path)) {
                    $this->blobClient->createBlockBlob($container, $filename, fopen($path, "r"));
                }
            }
        } catch (ServiceException $e) {
            throw new RuntimeException(sprintf("There was an error during uploading files:\n %s", $e->getMessage()), $e->getCode(), $e);
        }
    }

    /**
     * Download a file from a cloud
     * @param  string $save_to
     * @param  array $options
     */
    public function download(string $save_to, array $options): void
    {
        $container = $options['container'];
        $blob = $options['blob'];

        try {
            $getBlobResult = $this->blobClient->getBlob($container, $blob);
        } catch (ServiceException $e) {
            throw new RuntimeException(sprintf("There was an error during uploading files:\n %s", $e->getMessage()), $e->getCode(), $e);
        }

        file_put_contents($save_to, $getBlobResult->getContentStream());
    }
}

After that, pass a associative array to the "save" method:

$connectionString = 'DefaultEndpointsProtocol=https;AccountName=>>>yourAccount<<<;AccountKey=>>>yourKey<<<';

$microsoft_azure = new MicrosoftAzure($connectionString);
$microsoft_azure_upload_options = [
    'container' => 'your_container'
];

$to_microsoft_azure = [
    'cloud' => $microsoft_azure,
    'options' => $microsoft_azure_upload_options
];

$dash->save(null, $to_microsoft_azure);

A path can also be passed to save a copy of files on your local machine.

$hls->save('/var/www/media/videos/hls/test.m3u8', $to_microsoft_azure);

TO a Custom Cloud

You can upload your file to a custom cloud. You can create a custom cloud by implementing "CloudInterface":

use Streaming\Clouds\CloudInterface;

class CustomCloud implements CloudInterface
{

    /**
     * Upload a entire directory to a cloud
     * @param  string $dir
     * @param  array $options
     */
    public function uploadDirectory(string $dir, array $options): void
    {
        // TODO: Implement uploadDirectory() method.
    }

    /**
     * Download a file from a cloud
     * @param  string $save_to
     * @param  array $options
     */
    public function download(string $save_to, array $options): void
    {
        // TODO: Implement download() method.
    }
}

After creating your own cloud class, you can easily pass an array of the cloud options to the "save" method:

$custom_cloud = new CustomCloud();
// The array below will be passed to the `uploadDirectory` method in the `CustomCloud` class.
$custom_cloud_upload_options = [
    YOUR_OPTIONS => YOUR_VALUE_OPTIONS
];

$to_custom_cloud = [
    'cloud' => $custom_cloud,
    'options' => $custom_cloud_upload_options
];

$video = $dash->save(null, $to_custom_cloud);

TO multiple Clouds

You can save your files to multiple clouds:

$dash->save(null, [$to_aws_cloud, $to_google_cloud, $to_microsoft_azure, $to_custom_cloud]);

A path can also be passed to save a copy of files on your local machine.

$hls->save('/var/www/media/videos/hls/test.m3u8', [$to_google_cloud, $to_custom_cloud]);

If you like this project, please ⭐️ Star it on the Github.