W3docs

Subida de archivos en PHP

Aprende a gestionar la subida de archivos en PHP con $_FILES y move_uploaded_file() de forma segura y eficiente.

La subida de archivos es un requisito habitual en el desarrollo web. Ya sea una foto de perfil, un documento PDF o una importación CSV, permitir que los usuarios envíen archivos a tu servidor es una función central de la mayoría de las aplicaciones. En PHP esto se gestiona con la función move_uploaded_file() junto con el array superglobal $_FILES.

Este capítulo cubre el flujo completo de subida: configurar el formulario HTML, leer los metadatos del archivo subido desde $_FILES, validarlo de forma segura y moverlo a su destino final. También se explican los códigos de error de subida y las opciones de configuración de PHP que controlan los límites de subida.

Cómo funciona la subida de archivos en PHP

La subida de un archivo ocurre en tres etapas:

  1. El navegador envía el archivo en una solicitud POST de tipo multipart/form-data.
  2. PHP recibe el archivo y lo escribe en una ubicación temporal en disco, y expone sus detalles en el array $_FILES.
  3. Tu script valida el archivo y lo mueve desde la ubicación temporal a una permanente con move_uploaded_file().

El archivo temporal se elimina automáticamente al finalizar la solicitud si no lo mueves, por lo que debes procesarlo durante la misma solicitud.

El superglobal $_FILES

Cuando se sube un archivo, la información sobre él se almacena en el array superglobal $_FILES. Para un campo de formulario llamado userfile, el array contiene las siguientes claves:

  • $_FILES['userfile']['name'] - El nombre original del archivo subido.
  • $_FILES['userfile']['type'] - El tipo MIME del archivo subido.
  • $_FILES['userfile']['size'] - El tamaño del archivo subido en bytes.
  • $_FILES['userfile']['tmp_name'] - La ubicación temporal del archivo subido en el servidor.
  • $_FILES['userfile']['error'] - Un código de error que indica si hubo algún problema durante la subida (véanse los códigos de error a continuación).

Paso 1: El formulario HTML

El atributo enctype del formulario debe establecerse en multipart/form-data, y el método debe ser POST. Sin multipart/form-data, el navegador envía solo el nombre del archivo, no su contenido, y $_FILES estará vacío.

<form action="upload.php" method="POST" enctype="multipart/form-data">
  <input type="file" name="userfile">
  <input type="submit" value="Upload">
</form>

Paso 2: Validar la subida

Nunca confíes en un archivo subido. Antes de moverlo, comprueba tres cosas: que la subida se realizó correctamente, que el tamaño está dentro de tu límite y que el archivo es realmente del tipo que esperas.

Comprobar el código de error

Inspecciona siempre $_FILES['userfile']['error'] primero. PHP define constantes con nombre para los posibles valores:

ConstanteValorSignificado
UPLOAD_ERR_OK0Sin error, el archivo se subió correctamente.
UPLOAD_ERR_INI_SIZE1El archivo supera upload_max_filesize en php.ini.
UPLOAD_ERR_FORM_SIZE2El archivo supera el campo MAX_FILE_SIZE del formulario.
UPLOAD_ERR_PARTIAL3El archivo solo se subió parcialmente.
UPLOAD_ERR_NO_FILE4No se subió ningún archivo.
UPLOAD_ERR_NO_TMP_DIR6Falta una carpeta temporal.
UPLOAD_ERR_CANT_WRITE7No se pudo escribir el archivo en disco.

Validar tipo y tamaño de forma segura

No confíes en $_FILES['userfile']['type']. Ese valor lo proporciona el navegador y un atacante puede falsificarlo trivialmente. En su lugar, detecta el tipo MIME real a partir del contenido del archivo con la extensión finfo, y aplica tú mismo un límite de tamaño:

$file = $_FILES['userfile'];

// 1. Did the upload succeed?
if ($file['error'] !== UPLOAD_ERR_OK) {
  exit("Upload failed with error code " . $file['error']);
}

// 2. Enforce a maximum size (2 MB here).
$maxBytes = 2 * 1024 * 1024;
if ($file['size'] > $maxBytes) {
  exit("File is too large.");
}

// 3. Detect the real MIME type, not the client-supplied one.
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime  = $finfo->file($file['tmp_name']);

$allowed = [
  'image/jpeg' => 'jpg',
  'image/png'  => 'png',
  'image/gif'  => 'gif',
];

if (!isset($allowed[$mime])) {
  exit("Only JPEG, PNG, and GIF images are allowed.");
}

Paso 3: Mover el archivo a su destino final

Una vez validado el archivo, muévelo con move_uploaded_file(). Acepta dos argumentos: la ruta temporal ($_FILES['userfile']['tmp_name']) y la ruta de destino. Usar esta función en lugar de copy() o rename() es importante — verifica que el archivo fue una subida HTTP genuina, lo que impide que un atacante engañe a tu script para mover un archivo arbitrario del servidor.

Genera el nombre final del archivo tú mismo en lugar de confiar en el nombre original. Esto evita ataques de traversal de directorios (un nombre como ../../config.php) y la sobreescritura de archivos existentes:

$targetDir = "uploads/";

// Build a safe, unique file name; never trust the client's name.
$extension  = $allowed[$mime];
$safeName   = bin2hex(random_bytes(8)) . "." . $extension;
$targetFile = $targetDir . $safeName;

if (move_uploaded_file($file['tmp_name'], $targetFile)) {
  echo "The file was uploaded as " . $safeName;
} else {
  echo "There was an error saving the file.";
}

Para más detalles sobre esta función y su complemento is_uploaded_file(), consulta los capítulos de referencia move_uploaded_file() e is_uploaded_file().

Configuración que afecta a las subidas

Algunas directivas de php.ini limitan silenciosamente lo que tu script puede recibir. Si las subidas de archivos grandes fallan aunque tu código sea correcto, comprueba estas:

  • file_uploads — debe estar en On para que las subidas funcionen.
  • upload_max_filesize — el tamaño máximo individual de archivo que PHP aceptará (por defecto 2M).
  • post_max_size — el tamaño máximo total del cuerpo POST; debe ser mayor que upload_max_filesize.
  • max_file_uploads — el número máximo de archivos en una sola solicitud.

Conclusión

La subida de archivos en PHP es una parte fundamental del desarrollo web. Con el array superglobal $_FILES y la función move_uploaded_file(), puedes gestionar subidas en pocas líneas. Lo difícil es hacerlo de forma segura: comprueba siempre el código de error de subida, aplica un límite de tamaño, detecta el tipo MIME real con finfo en lugar de confiar en $_FILES[...]['type'], y genera tu propio nombre de archivo para que el cliente nunca pueda controlar dónde aterriza el archivo. Para procesar el archivo guardado posteriormente, consulta gestión de archivos en PHP y validación de formularios.

Práctica

Práctica
¿Cuáles de las siguientes afirmaciones sobre la subida de archivos en PHP son correctas?
¿Cuáles de las siguientes afirmaciones sobre la subida de archivos en PHP son correctas?
Was this page helpful?