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 */ private function candidatePaths(string $path): array { $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 */ 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; } }