Skip to content

Instantly share code, notes, and snippets.

@matheusb-comp
Created October 26, 2020 13:26
Show Gist options
  • Save matheusb-comp/69fbdaefad76dd5173f304d17ee80ed3 to your computer and use it in GitHub Desktop.
Save matheusb-comp/69fbdaefad76dd5173f304d17ee80ed3 to your computer and use it in GitHub Desktop.
PHP class to download files on Laravel, with size limit based on the Content-Type header
<?php
namespace App\Services;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\RequestException;
use Psr\Http\Message\ResponseInterface;
use Illuminate\Support\Facades\Storage;
class FileDownloaderService
{
/**
* Base size limit for downloads: 4.5MB (in bytes).
*
* @var int
*/
public const MAX_BYTES_BASE = 4718592;
/**
* Video size limit for downloads: 45MB (in bytes).
*
* @var int
*/
public const MAX_BYTES_VIDEO = 47185920;
/**
* MIME types to use the video size limit.
*
* @var array
*/
public const MIMES_VIDEO = [
'video/mp4',
'video/3gpp',
'video/quicktime',
];
/**
* Guzzle HTTP client.
*
* @var GuzzleHttp\Client
*/
protected $http;
public function __construct($options = [])
{
// Default options for the Guzzle HTTP client
$defaultOptions = [
// Type 4: Static class method call (As of PHP 5.2.3)
// https://www.php.net/manual/en/language.types.callable.php
'on_headers' => self::class . '::checkDownloadSize',
];
// Create the HTTP client
$this->http = new Client(array_merge($defaultOptions, $options));
}
public function download($url, $destination, $visibility = null)
{
// Send a GET request and save the result on a local file
try {
$res = $this->http->get($url);
return Storage::put($destination, $res->getBody(), $visibility);
} catch (RequestException $e) {
// Unrwap exceptions (such the ones from 'on_headers')
$previous = $e->getPrevious();
throw ($previous) ? $previous : $e;
}
}
public static function checkDownloadSize(ResponseInterface $response)
{
// Get the type and size headers from the response
$type = $response->getHeader('Content-Type')[0] ?? null;
$size = (int) $response->getHeader('Content-Length')[0] ?? null;
// Calculate the maximum allowed number of bytes
$max = self::MAX_BYTES_BASE;
if (collect(self::MIMES_VIDEO)->contains($type)) {
$max = self::MAX_BYTES_VIDEO;
}
// Throw an exception when the size does not exist or exceeds the limit
if (!$size || $size > $max) {
throw new Exception(
'File too big (bytes: ' . $size .
'). Limit (in bytes): ' . $max
);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment