Created
October 26, 2020 13:26
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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