Files
alrahma_sunday_school_api/app/Services/Files/FileServeService.php
T
2026-06-09 00:03:03 -04:00

110 lines
3.6 KiB
PHP

<?php
namespace App\Services\Files;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Symfony\Component\HttpKernel\Exception\HttpException;
class FileServeService
{
public function meta(string $baseDir, string $name, array $allowedExtensions, ?string $downloadName = null): array
{
return $this->resolveFile($baseDir, $name, $allowedExtensions, $downloadName);
}
public function serveInline(
string $baseDir,
string $name,
array $allowedExtensions,
Request $request,
?string $downloadName = null,
bool $nosniff = false
): Response {
$meta = $this->resolveFile($baseDir, $name, $allowedExtensions, $downloadName);
$ifNoneMatch = trim((string) $request->headers->get('If-None-Match', ''), '"');
$ifModifiedSince = (string) $request->headers->get('If-Modified-Since', '');
$imsTime = $ifModifiedSince !== '' ? strtotime($ifModifiedSince) : false;
if (($ifNoneMatch !== '' && $ifNoneMatch === $meta['etag']) ||
($imsTime !== false && $imsTime >= $meta['mtime'])
) {
return response('', 304, $this->notModifiedHeaders($meta));
}
$headers = [
'Content-Type' => $meta['mime'],
'Content-Disposition' => 'inline; filename="'.$meta['download_name'].'"',
'Content-Length' => (string) $meta['size'],
'ETag' => $meta['etag'],
'Last-Modified' => $meta['last_modified'],
'Cache-Control' => 'private, max-age=300',
'X-Content-Type-Options' => 'nosniff',
];
return response(file_get_contents($meta['path']), 200, $headers);
}
private function resolveFile(string $baseDir, string $name, array $allowedExtensions, ?string $downloadName): array
{
if ($name !== basename($name)) {
throw new HttpException(400, 'Invalid filename');
}
$ext = strtolower(pathinfo($name, PATHINFO_EXTENSION));
if (! in_array($ext, $allowedExtensions, true)) {
throw new HttpException(404, 'File not found');
}
$path = rtrim($baseDir, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$name;
if (! is_file($path)) {
throw new HttpException(404, 'File not found');
}
$mime = $this->detectMime($path);
$mtime = filemtime($path) ?: time();
$size = filesize($path) ?: 0;
$etag = md5($name.'|'.$mtime.'|'.$size);
$downloadName = $downloadName ? $downloadName.'.'.$ext : $name;
return [
'name' => $name,
'path' => $path,
'mime' => $mime,
'mtime' => $mtime,
'size' => $size,
'etag' => $etag,
'download_name' => $downloadName,
'last_modified' => gmdate('D, d M Y H:i:s', $mtime).' GMT',
];
}
private function detectMime(string $path): string
{
$mime = 'application/octet-stream';
if (function_exists('finfo_open')) {
$fi = finfo_open(FILEINFO_MIME_TYPE);
if ($fi) {
$detected = finfo_file($fi, $path);
if ($detected) {
$mime = $detected;
}
finfo_close($fi);
}
} elseif (function_exists('mime_content_type')) {
$mime = mime_content_type($path) ?: $mime;
}
return $mime;
}
private function notModifiedHeaders(array $meta): array
{
return [
'ETag' => $meta['etag'],
'Last-Modified' => $meta['last_modified'],
];
}
}