2026-06-29 20:16:28 +03:30
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Http\Controllers;
|
|
|
|
|
|
|
|
|
|
use Illuminate\Http\Request;
|
|
|
|
|
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
|
|
|
|
|
|
|
|
|
class PublicStorageController extends Controller
|
|
|
|
|
{
|
|
|
|
|
public function show(Request $request, string $path): BinaryFileResponse
|
|
|
|
|
{
|
|
|
|
|
$path = $this->sanitizeRelativePath($path);
|
|
|
|
|
if ($path === '') {
|
|
|
|
|
abort(404);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$fullPath = $this->resolveReadablePath($path);
|
|
|
|
|
if ($fullPath === null) {
|
|
|
|
|
abort(404);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return response()->file($fullPath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function sanitizeRelativePath(string $path): string
|
|
|
|
|
{
|
|
|
|
|
$path = str_replace('\\', '/', $path);
|
|
|
|
|
$path = ltrim($path, '/');
|
|
|
|
|
|
|
|
|
|
if ($path === '' || str_contains($path, '..')) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $path;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function resolveReadablePath(string $path): ?string
|
|
|
|
|
{
|
|
|
|
|
foreach ($this->candidatePaths($path) as $fullPath) {
|
|
|
|
|
if (is_file($fullPath) && is_readable($fullPath)) {
|
|
|
|
|
return $fullPath;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return list<string>
|
|
|
|
|
*/
|
|
|
|
|
private function candidatePaths(string $path): array
|
|
|
|
|
{
|
2026-08-26 16:59:27 +03:30
|
|
|
$relative = [$path, ...$this->alternateExtensionPaths($path)];
|
|
|
|
|
$full = [];
|
|
|
|
|
foreach ($relative as $rel) {
|
|
|
|
|
$full[] = storage_path('app/public/'.$rel);
|
|
|
|
|
$full[] = storage_path('app/public/storage/'.$rel);
|
|
|
|
|
$full[] = public_path('storage/'.$rel);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $full;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* آپلود ادمین WebP میسازد ولی const گاهی همان هش را با .png نگه میدارد.
|
|
|
|
|
*
|
|
|
|
|
* @return list<string>
|
|
|
|
|
*/
|
|
|
|
|
private function alternateExtensionPaths(string $path): array
|
|
|
|
|
{
|
|
|
|
|
if (! preg_match('/^(.*)\.([a-z0-9]+)$/i', $path, $matches)) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$stem = $matches[1];
|
|
|
|
|
$ext = strtolower($matches[2]);
|
|
|
|
|
$out = [];
|
|
|
|
|
foreach (['webp', 'png', 'jpg', 'jpeg', 'gif'] as $alt) {
|
|
|
|
|
if ($alt !== $ext) {
|
|
|
|
|
$out[] = $stem.'.'.$alt;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $out;
|
2026-06-29 20:16:28 +03:30
|
|
|
}
|
|
|
|
|
}
|