Created
October 26, 2021 09:22
-
-
Save eghojansu/4cf5fccaa73118ff4703ff9c810eb79c to your computer and use it in GitHub Desktop.
Symfony static file server
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\Service; | |
use App\Utils; | |
use Symfony\Component\HttpFoundation\Request; | |
use Symfony\Component\HttpFoundation\Response; | |
use Symfony\Component\HttpFoundation\StreamedResponse; | |
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; | |
final class FileServer | |
{ | |
public function createResponse(Request $request, string $root, string $path, string $prefix = '', array $headers = null): Response | |
{ | |
$file = $root . $path; | |
if (!is_readable($file) || false === strpos(Utils::fixslashes(realpath($file)), Utils::fixslashes($root))) { | |
throw new NotFoundHttpException(sprintf("File not found: '%s'", $prefix . $path)); | |
} | |
$response = new Response('', 200, ($headers ?? array()) + array( | |
'Last-Modified' => gmdate('D, d M Y H:i:s', filemtime($file)) . ' GMT', | |
)); | |
if ($response->isNotModified($request)) { | |
return $response; | |
} | |
$size = filesize($file); | |
$range = $this->getRequestRange($request, $size); | |
if (!$range) { | |
$response->setStatusCode(416); | |
$response->headers->set('Content-Range', sprintf('bytes */%u', $size)); | |
return $response; | |
} | |
return $this->createStreamedResponse($response, $file, $size, $range); | |
} | |
private function createStreamedResponse(Response $base, string $file, int $size, array $range): StreamedResponse | |
{ | |
list($min, $max, $isPartial) = $range + array(2 => false); | |
$len = $max - $min + 1; | |
$code = $base->getStatusCode(); | |
$headers = array( | |
'Accept-Ranges' => 'bytes', | |
'Content-Length' => $len, | |
'Content-Type' => Utils::getMimeType($file), | |
) + $base->headers->allPreserveCase(); | |
if ($isPartial) { | |
$code = 206; | |
$headers['Content-Range'] = sprintf('bytes %u-%u/%u', $min, $max, $size); | |
} | |
return new StreamedResponse(static function () use ($file, $len, $min) { | |
$sent = 0; | |
$fp = fopen($file, 'rb'); | |
if ($min > 0) { | |
fseek($fp, $min); | |
} | |
while (!feof($fp) && (connection_status() === CONNECTION_NORMAL)) { | |
$readingSize = $len - $sent; | |
$readingSize = min($readingSize, 512 * 1024); | |
if ($readingSize <= 0) { | |
break; | |
} | |
$data = fread($fp, $readingSize); | |
if(!$data) { | |
break; | |
} | |
$sent += strlen($data); | |
echo $data; | |
flush(); | |
} | |
fclose($fp); | |
}, $code, $headers); | |
} | |
private function getRequestRange(Request $request, int $size): ?array | |
{ | |
$max = $size - 1; | |
if (!$request->headers->has('Range')) { | |
return array(0, $max); | |
} | |
$requestRange = $request->headers->get('Range'); | |
if ('bytes=' !== substr($requestRange, 0, 6)) { | |
return null; | |
} | |
$ranges = explode(',', substr($requestRange, 6)); | |
$range = explode('-', $ranges[0]); | |
if ($range[0] === '') { | |
$range[0] = 0; | |
} | |
if ($range[1] === '') { | |
$range[1] = $max; | |
} | |
$isPartial = ($range[0] >= 0) && ($range[1] <= $max) && ($range[0] <= $range[1]); | |
if (!$isPartial) { | |
return null; | |
} | |
$range[2] = $isPartial; | |
return $range; | |
} | |
} |
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; | |
use Symfony\Component\Mime\MimeTypes; | |
final class Utils | |
{ | |
public static function fixslashes(string $str): string | |
{ | |
return strtr($str, '\\', '/'); | |
} | |
public static function getMimeType(string $filename): string | |
{ | |
$mimeTypes = new MimeTypes(); | |
$extension = pathinfo($filename, PATHINFO_EXTENSION); | |
$types = $mimeTypes->getMimeTypes($extension); | |
if ($types) { | |
return reset($types); | |
} | |
return $mimeTypes->guessMimeType($filename) ?? ''; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment