'Archivo inválido']); exit; } // Solo archivos con prefijo de orden (seguridad adicional) if (!preg_match('/^orden_[a-zA-Z0-9_]+\.[a-zA-Z0-9]+$/', $filename)) { http_response_code(403); echo json_encode(['error' => 'Acceso denegado']); exit; } $path = realpath(__DIR__ . '/../../uploads/media/' . $filename); $base = realpath(__DIR__ . '/../../uploads/media'); // Verificar que el archivo esté dentro del directorio permitido if (!$path || !$base || strpos($path, $base) !== 0 || !is_file($path)) { http_response_code(404); echo json_encode(['error' => 'Archivo no encontrado']); exit; } // Detectar MIME real $finfo = finfo_open(FILEINFO_MIME_TYPE); $mime = finfo_file($finfo, $path); finfo_close($finfo); // Mapa de extensiones como fallback $ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION)); $mimeMap = [ 'pdf' => 'application/pdf', 'doc' => 'application/msword', 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'jpg' => 'image/jpeg', 'jpeg' => 'image/jpeg', 'png' => 'image/png', 'gif' => 'image/gif', 'webp' => 'image/webp', ]; if (!$mime || $mime === 'application/octet-stream') { $mime = $mimeMap[$ext] ?? 'application/octet-stream'; } // Tipos que el navegador puede mostrar inline $inlineTypes = ['application/pdf', 'image/jpeg', 'image/png', 'image/gif', 'image/webp']; $disposition = in_array($mime, $inlineTypes) ? 'inline' : 'attachment'; // Si el archivo fue guardado con extensión .bin, usar la correcta según MIME $mimeToExt = [ 'application/pdf' => 'pdf', 'application/msword' => 'doc', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx', 'image/jpeg' => 'jpg', 'image/png' => 'png', 'image/gif' => 'gif', 'image/webp' => 'webp', ]; $downloadExt = ($ext === 'bin' && isset($mimeToExt[$mime])) ? $mimeToExt[$mime] : $ext; $downloadName = substr($filename, 0, strrpos($filename, '.') + 1) . $downloadExt; header('Content-Type: ' . $mime); header('Content-Disposition: ' . $disposition . '; filename="' . rawurlencode($downloadName) . '"'); header('Content-Length: ' . filesize($path)); header('Cache-Control: private, max-age=3600'); header('X-Content-Type-Options: nosniff'); readfile($path); exit;