Open a File from Clouds

Last updated: October 11th, 2019

Open a File From Clouds

Form a Cloud using Guzzle

First you need to install Guzzle via composer:

composer require guzzlehttp/guzzle:~6.0

After installing the package, you should 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 "openFromCloud" method:

$cloud = new MyCloud('https://www.aminyazdanpanah.com/my_sweetie.mp4');
$from_cloud = ['cloud' => $cloud];

$video = $ffmpeg->openFromCloud($from_cloud);
A path to save the file, the method of request, and request options can also be passed to the class.

$api = 'https://www.aminyazdanpanah.com/api/v1.0';
$save_to = '/var/www/media/videos/my_sweetie.mp4';
$method = 'POST';
$current_percentage = 0;
$options = [
    'auth' => ['username', 'password', 'digest'],
    'form_params' => [
        'user'      => 'USER_ID',
        'method'    => 'download',
        'file'      => ['dir3', 'videos', 'my_sweetie.mp4']
    ],
    'headers' => [
        'User-Agent'        => 'Mozilla/5.0 (compatible; AminYazdanpanahBot/1.0; +http://aminyazdanpanah.com/bots)',
        'Accept'            => 'application/json',
        'Authorization'     => 'Bearer ACCESS_TOKEN'
    ],
    'progress' => function(
        $downloadTotal,
        $downloadedBytes,
        $uploadTotal,
        $uploadedBytes
    ) use (&$current_percentage) {
        $percentage = ($downloadTotal > 500) ? intval(($downloadedBytes / $downloadTotal) * 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 Downloading... (%s%%)[%s%s]", $percentage, str_repeat('#', $percentage), str_repeat('-', (100 - $percentage)));
            $current_percentage = $percentage;
        }
    },
];

$cloud = new \Streaming\Clouds\Cloud($api, $method, $options);
$from_cloud = ['cloud' => $cloud];

$video = $ffmpeg->openFromCloud($from_cloud, $save_to);

From Amazon S3

Amazon S3 or Amazon Simple Storage Service is a service offered by Amazon Web Services (AWS) that provides object storage through a web service interface. Learn more

First you need to install AWS SDK via composer:

composer require aws/aws-sdk-php

After installing the package, you should 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 "openFromCloud" 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_download_options = [
    'Bucket' => 'my-bucket-name',
    'Key' => '/videos/my_sweetie.mp4'
];

$from_aws_cloud = [
    'cloud' => $aws_cloud,
    'options' => $aws_cloud_download_options
];

$video = $ffmpeg->openFromCloud($from_aws_cloud);

NOTE:

For getting credentials, you need to have an AWS account or you can create one.

NOTE:

Before you get started, please read the "AWS SDK for PHP" Document found here.

NOTE:

A path can also be passed to save the file on your local machine.

From Google Cloud Storage

Google Cloud Storage is a RESTful online file storage web service for storing and accessing data on Google Cloud Platform infrastructure. The service combines the performance and scalability of Google's cloud with advanced security and sharing capabilities. It is an Infrastructure as a Service (IaaS), comparable to Amazon S3 online storage service. Contrary to Google Drive and according to different service specifications, Google Cloud Storage appears to be more suitable for enterprises. Learn more

First you need to install Google Cloud Storage via composer:

composer require google/cloud-storage

After installing the package, you should 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 "openFromCloud" method:

$config = [
    'keyFilePath' => '/path/to/credentials.json' // Alternativaely, you can authenticate by setting the environment variable. See https://cloud.google.com/docs/authentication/production#auth-cloud-implicit-php
];
$bucket = 'yourbucket';

$google_cloud = new GoogleCloudStorage($config, $bucket);
$google_cloud_download_options = [
    'filename' => 'my_sweetie.mp4',
    OTHER_OPTION => OTHER_VALUE_OPTION
];

$from_google_cloud = [
    'cloud' => $google_cloud,
    'options' => $google_cloud_download_options
];

$video = $ffmpeg->openFromCloud($from_google_cloud);

NOTE:

For creating credentials, read the Cloud Storage Authentication found here or you can create it directly (Select the "Service account key" option).

NOTE:

A path can also be passed to save the file on your local machine.

From Microsoft Azure Storage

Azure Storage is Microsoft's cloud storage solution for modern data storage scenarios. Azure Storage offers a massively scalable object store for data objects, a file system service for the cloud, a messaging store for reliable messaging, and a NoSQL store. Learn more

First you need to install Microsoft Azure Storage via composer:

composer require microsoft/azure-storage-blob

After installing the package, you should 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 "openFromCloud" method:

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

$microsoft_azure = new MicrosoftAzure($connectionString);
$microsoft_azure_download_options = [
    'container' => 'your_container',
    'blob' => 'my_sweetie.mp4'
];

$from_microsoft_azure = [
    'cloud' => $microsoft_azure,
    'options' => $microsoft_azure_download_options
];

$video = $ffmpeg->openFromCloud($from_microsoft_azure);

NOTE:

To authenticate the service, please see here.

NOTE:

A path can also be passed to save the file on your local machine.

From a Custom Cloud

You can create a custom cloud by implementing the "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 the cloud options to the openFromCloud method:

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

$from_custom_cloud = [
    'cloud' => $custom_cloud,
    'options' => $custom_cloud_download_options
];

$video = $ffmpeg->openFromCloud($from_custom_cloud);

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