descargar_orden: reescribir sin _helpers para evitar ob_start y Content-Type:json; fix .bin -> extension real
This commit is contained in:
+139
-6
@@ -1,14 +1,147 @@
|
||||
<?php
|
||||
/**
|
||||
* GET /api/lab/descargar_orden.php?file=ruta_relativa_o_nombre
|
||||
* Sirve archivos de órdenes médicas con el Content-Type correcto.
|
||||
* Sirve archivos de órdenes médicas con el Content-Type y nombre de archivo correctos.
|
||||
*
|
||||
* El parámetro 'file' puede ser:
|
||||
* - Un nombre simple: orden_20240101_abcd.pdf
|
||||
* - Una ruta relativa: uploads/media/2026/03/m_XXXX.bin
|
||||
* NO usa _helpers.php para evitar que ob_start() interfiera con readfile()
|
||||
* y que Content-Type:application/json se envíe antes del tipo real del archivo.
|
||||
*/
|
||||
require_once __DIR__ . '/_helpers.php';
|
||||
requireMethod('GET');
|
||||
|
||||
// Sin buffering de salida — el archivo debe fluir directamente al navegador
|
||||
if (ob_get_level()) ob_end_clean();
|
||||
|
||||
require_once __DIR__ . '/../../config/config.php';
|
||||
|
||||
// Seguridad: el usuario debe estar autenticado
|
||||
if (!isUserLoggedIn()) {
|
||||
http_response_code(403);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['error' => 'No autorizado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'GET') {
|
||||
http_response_code(405);
|
||||
exit;
|
||||
}
|
||||
|
||||
$fileParam = trim($_GET['file'] ?? '');
|
||||
|
||||
// Rechazar rutas con traversal
|
||||
if (!$fileParam || strpos($fileParam, '..') !== false) {
|
||||
http_response_code(400);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['error' => 'Parámetro inválido']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$projectRoot = realpath(__DIR__ . '/../../');
|
||||
$uploadsBase = realpath($projectRoot . '/uploads');
|
||||
|
||||
// Resolver ruta física
|
||||
if (preg_match('#^uploads/#', $fileParam)) {
|
||||
$path = realpath($projectRoot . '/' . $fileParam);
|
||||
} elseif (preg_match('/^[a-zA-Z0-9_\-\.]+$/', $fileParam)) {
|
||||
$path = realpath($projectRoot . '/uploads/media/' . $fileParam);
|
||||
} else {
|
||||
http_response_code(400);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['error' => 'Nombre de archivo inválido']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Verificar que existe y está dentro de uploads/
|
||||
if (!$path || !$uploadsBase || strpos($path, $uploadsBase) !== 0 || !is_file($path)) {
|
||||
http_response_code(404);
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode(['error' => 'Archivo no encontrado']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── Detección MIME en 3 capas ─────────────────────────────────────────
|
||||
$mime = '';
|
||||
|
||||
// Capa 1: finfo
|
||||
if (function_exists('finfo_open')) {
|
||||
$fi = finfo_open(FILEINFO_MIME_TYPE);
|
||||
$mime = $fi ? (string)finfo_file($fi, $path) : '';
|
||||
if ($fi) finfo_close($fi);
|
||||
// Quitar parámetros extras (ej: "image/jpeg; charset=binary")
|
||||
if ($mime && strpos($mime, ';') !== false) {
|
||||
$mime = trim(explode(';', $mime)[0]);
|
||||
}
|
||||
}
|
||||
|
||||
// Capa 2: magic bytes manuales (más fiable que finfo para archivos renombrados como .bin)
|
||||
if (!$mime || $mime === 'application/octet-stream') {
|
||||
$fh = fopen($path, 'rb');
|
||||
$bytes = $fh ? fread($fh, 16) : '';
|
||||
if ($fh) fclose($fh);
|
||||
|
||||
if (substr($bytes, 0, 3) === "\xFF\xD8\xFF") {
|
||||
$mime = 'image/jpeg';
|
||||
} elseif (substr($bytes, 0, 8) === "\x89PNG\r\n\x1a\n") {
|
||||
$mime = 'image/png';
|
||||
} elseif (substr($bytes, 0, 5) === '%PDF-') {
|
||||
$mime = 'application/pdf';
|
||||
} elseif (substr($bytes, 0, 6) === 'GIF89a' || substr($bytes, 0, 6) === 'GIF87a') {
|
||||
$mime = 'image/gif';
|
||||
} elseif (substr($bytes, 0, 4) === 'RIFF' && substr($bytes, 8, 4) === 'WEBP') {
|
||||
$mime = 'image/webp';
|
||||
} elseif (substr($bytes, 0, 4) === "\xD0\xCF\x11\xE0") {
|
||||
$mime = 'application/msword'; // DOC clásico
|
||||
} elseif (substr($bytes, 0, 2) === 'PK') {
|
||||
$mime = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; // DOCX/ZIP
|
||||
}
|
||||
}
|
||||
|
||||
// Capa 3: fallback por extensión del nombre original
|
||||
$ext = strtolower(pathinfo($fileParam, PATHINFO_EXTENSION));
|
||||
if (!$mime || $mime === 'application/octet-stream') {
|
||||
$mimeByExt = [
|
||||
'pdf' => 'application/pdf',
|
||||
'jpg' => 'image/jpeg',
|
||||
'jpeg' => 'image/jpeg',
|
||||
'png' => 'image/png',
|
||||
'gif' => 'image/gif',
|
||||
'webp' => 'image/webp',
|
||||
'doc' => 'application/msword',
|
||||
'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
];
|
||||
$mime = $mimeByExt[$ext] ?? 'application/octet-stream';
|
||||
}
|
||||
|
||||
// ── Nombre de descarga: si era .bin, usar la extensión real ──────────
|
||||
$extByMime = [
|
||||
'application/pdf' => 'pdf',
|
||||
'image/jpeg' => 'jpg',
|
||||
'image/png' => 'png',
|
||||
'image/gif' => 'gif',
|
||||
'image/webp' => 'webp',
|
||||
'application/msword' => 'doc',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' => 'docx',
|
||||
];
|
||||
$basename = basename($fileParam);
|
||||
$downloadName = $basename;
|
||||
if ($ext === 'bin' && isset($extByMime[$mime])) {
|
||||
$stem = substr($basename, 0, strrpos($basename, '.'));
|
||||
$downloadName = $stem . '.' . $extByMime[$mime];
|
||||
}
|
||||
|
||||
// ── Disposición: inline para imágenes/PDF, attachment para el resto ──
|
||||
$inlineMimes = ['application/pdf', 'image/jpeg', 'image/png', 'image/gif', 'image/webp'];
|
||||
$disposition = in_array($mime, $inlineMimes, true) ? 'inline' : 'attachment';
|
||||
|
||||
// ── Enviar archivo ───────────────────────────────────────────────────
|
||||
header('Content-Type: ' . $mime);
|
||||
header('Content-Disposition: ' . $disposition . '; filename="' . addslashes($downloadName) . '"');
|
||||
header('Content-Length: ' . filesize($path));
|
||||
header('Cache-Control: private, max-age=3600');
|
||||
header('X-Content-Type-Options: nosniff');
|
||||
|
||||
readfile($path);
|
||||
exit;
|
||||
|
||||
|
||||
$fileParam = $_GET['file'] ?? '';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user