Files
dockerfluxodecaixapro/app/Controllers/StorageController.php
T
2026-09-01 10:46:29 -03:00

99 lines
3.2 KiB
PHP
Executable File

<?php
namespace App\Controllers;
use Kernel\View;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
class StorageController extends BaseController
{
/**
* Serve a file from storage/uploads/{module}/{filename}
*
* @param Request $request
* @param string $module
* @param string $filename
* @return Response
*/
public function serve(Request $request, string $module, string $filename): Response
{
// Sanitize inputs to prevent directory traversal
$module = preg_replace('/[^a-zA-Z0-9_-]/', '', $module);
// Allow subfolders in filename (e.g. "123/image.jpg")
$filename = ltrim($filename, '/');
if (strpos($filename, '..') !== false) {
return new Response('Access denied.', 403);
}
// Build the physical path
$basePath = dirname(__DIR__, 2) . '/storage/uploads';
$filePath = $basePath . '/' . $module . '/' . $filename;
// Security check: Ensure the file is actually inside storage/uploads
$realPath = realpath($filePath);
if (!$realPath || strpos($realPath, realpath($basePath)) !== 0) {
return new Response('File not found or access denied.', 404);
}
if (!file_exists($realPath)) {
return new Response('File not found.', 404);
}
// Determine MIME type
$mimeType = $this->getMimeType($realPath);
// Create BinaryFileResponse for efficient file serving
$response = new BinaryFileResponse($realPath);
$response->headers->set('Content-Type', $mimeType);
$response->setContentDisposition(
ResponseHeaderBag::DISPOSITION_INLINE,
$filename
);
// Add cache headers (cache for 30 days)
$response->setPublic();
$response->setMaxAge(2592000);
$response->setSharedMaxAge(2592000);
return $response;
}
/**
* Get MIME type of a file
*/
private function getMimeType(string $path): string
{
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
$mimes = [
'jpg' => 'image/jpeg',
'jpeg' => 'image/jpeg',
'png' => 'image/png',
'gif' => 'image/gif',
'webp' => 'image/webp',
'svg' => 'image/svg+xml',
'pdf' => 'application/pdf',
'doc' => 'application/msword',
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'xls' => 'application/vnd.ms-excel',
'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
'txt' => 'text/plain',
'zip' => 'application/zip',
];
if (isset($mimes[$extension])) {
return $mimes[$extension];
}
if (function_exists('mime_content_type')) {
return mime_content_type($path);
}
return 'application/octet-stream';
}
}