Desarrollo

Cómo añadir archivos desde un formulario

Desarrollo HTML PHP

Dentro de nuestro bloque dedicado a los formularios hemos visto cómo solicitar datos al usuario y recogerlos después desde PHP. Posteriormente, realizamos un importante artículo dedicado a la seguridad en los formularios. Por último, nos centramos en la validación de datos mediante funciones básicas de PHP y en otras utilidades que pudiéramos definir y nos fueran útiles. Para cerrar esta primera gran toma de contacto con los formularios vamos a hablar sobre cómo añadir archivos desde un formulario.

Recuerda que puedes acceder al índice de contenidos para tener una referencia a todos los artículos sobre formularios que ya hemos publicado y todo lo que está por venir.

Primeros pasos para añadir archivos desde un formulario

Lo primero y fundamental que necesitamos para que podamos añadir archivos desde un formulario es añadir el atributo enctype a los mismos con el valor multipart/form-data. Ésto es algo obligatorio, pues si no es así, veremos que los archivos nunca nos llegarán. A veces es frecuente olvidarse de este paso y perder mucho tiempo pensando en por qué no funciona.

Posteriormente, es evidente que también necesitamos el <input> correspondiente con su atributo type indicando que es file, con lo que podemos así subir cualquier tipo de archivo, desde imágenes hasta PDF sin problemas.

Una primera limitación de tamaño

Pese a que no es obligatorio, sí se indica como muy conveniente que, en los formularios de subida de archivos, se indique justo antes del <input> correspondiente, otro <input> de tipo hidden (es decir, oculto) indicando como atributo name el valor MAX_FILE_SIZE y como atributo value el tamaño máximo del archivo a subir. Este valor debe ser expresado en bytes.

Como comentamos, no es obligatorio, pues el formulario funcionará igual si no añadimos este <input>, pero sí muy conveniente para limitar en un primer paso el tamaño del archivo y captar el error. Y es que podemos evitar esta limitación, pero luego nuestro servidor (sea local o ya en un hosting) va a tener una limitación, así que si no indicamos nada e intentamos subir un archivo más grande de lo que nos permite el servidor, no lo vamos a conseguir. Siempre, para ello, es mejor indicárselo al usuario que hacerle esperar para darle un error de tamaño máximo más adelante.

¿Cómo aumentar el tamaño máximo?

Como vimos en el artículo donde instalamos un servidor local, debemos ir al archivo php.ini y modificar los parámetros que comentamos en ese artículo. Pero, cuidado, si nos pasamos es posible que acabemos sobrecargando el servidor igualmente o no tengamos espacio para guardar el archivo.

Un formulario básico para subir archivos

Veamos un ejemplo básico para añadir archivos desde un formulario. Éste será muy básico y sólo contendrá los <inputs> necesarios. Por cierto, el método de subida debe ser un post, pues ya comentamos que con get no es posible subir archivos:

<form action="cargar.php" method="post" enctype="multipart/form-data">
  <input type="hidden" name="MAX_FILE_SIZE" value="4194304">
  <p>Adjunta un archivo: <input type="file" name="archivo"></p>
  <input type="submit" value="Adjuntar archivo">
</form>

Nada nuevo que no hayamos visto. Implementamos un formulario cuya acción se realizará en cargar.php con el método post y preparado para subir archivos. Colocamos el <input> de tipo hidden que hemos comentado, el <input> de tipo file que nos mostrará el típico botón para indicarle un archivo y un <input> de tipo submit para enviar el formulario. El valor máximo que hemos definido como ejemplo son 4194304 de bytes, es decir, 4 megas (1024 * 1024 * 4).

El array $_FILES

En esta ocasión no tendremos los arrays $_REQUEST o $_POST para obtener los datos del archivo, sino que debemos hacer algunas cosas más. En primer lugar, toda la información del archivo que hemos adjuntado en el formulario estará dentro del array $_FILES, concretamente en el ejemplo anterior tendremos el archivo subido en $_FILES[‘archivo’], pues el campo name del <input> será el que nos indicará el elemento en $_FILES. Pero este array contiene muchas más cosas.

$_FILES[‘archivo’][‘name’] contendrá el nombre original del archivo que hayamos subido. Es decir, si en nuestro ordenador teníamos el archivo vacaciones.jpg, aquí encontraremos guardado ese mismo nombre.

$_FILES[‘archivo’][‘type’] tendrá el tipo MIME del archivo. El tipo MIME podríamos definirlo rápidamente como si fuera la extensión del archivo, aunque con algo más de contenido que requiere que leamos más información de todos los posibles. En el ejemplo de vacaciones.jpg, el tipo MIME sería image/jpg, por lo que nos está indicando que es una imagen con extensión jpg.

$_FILES[‘archivo’][‘size’] nos indica el tamaño, en bytes, del archivo subido.

$_FILES[‘archivo’][‘tmp_name’] contiene el nombre temporal del archivo en el cual éste se almacena en el servidor temporalmente. Básicamente, cuando subimos un archivo lo que se hace es guardarlo en una carpeta temporal de nuestro servidor (definida también en php.ini), a la espera de que lo procesemos e indiquemos dónde queremos guardarlo de forma definitiva y qué nombre le pondremos. Así pues, aquí se guarda ese nombre temporal.

$_FILES[‘archivo’][‘error’] es el lugar donde se guardarán los posibles errores, o no, que hayan surgido a la hora de añadir archivos desde un formulario.

Antes de proseguir con la subida, veamos con qué códigos de error podemos encontrarnos.

Códigos de errores posibles

Como hemos dicho, en $_FILES[‘archivo’][‘error’] encontraremos los códigos de error que ha generado la subida, excepto por uno de ellos que indica exactamente lo contrario, es decir, que no ha habido error. Repasamos los posibles errores con sus valores (predefinidos y numéricos):

Código (constante)ValorExplicación
UPLOAD_ERR_OK0No ha habido error, el archivo se ha subido con éxito
UPLOAD_ERR_INI_SIZE1El archivo es mayor al tamaño máximo permitido por la directiva upload_max_filesize del archivo php.ini
UPLOAD_ERR_FORM_SIZE2El archivo es mayor al tamaño que le hemos indicado en MAX_FILE_SIZE dentro del formulario
UPLOAD_ERR_PARTIAL3Sólo se ha subido el archivo de forma parcial
UPLOAD_ERR_NO_FILE4No se ha podido subir el archivo
UPLOAD_ERR_NO_TMP_DIR6No existe la carpeta temporal donde debe guardarse temporalmente el archivo
UPLOAD_ERR_CANT_WRITE7No se ha podido escribir el archivo en el disco
UPLOAD_ERR_EXTENSION8Una extensión de PHP ha detenido la subida de archivos, sin embargo no puede especificar cuál ha sido

Lógica para la subida de archivos

Después de conocer cómo construir el formulario y el array $_FILES donde tenemos los datos del archivo subido, vamos a seguir con el siguiente paso para añadir archivos desde un formulario. Para ello, montaremos una pequeña lógica para realizar alguna validación y luego moveremos el archivo a su localización definitiva.

Validaciones

Antes de guardar definitivamente el fichero, vamos primero a validar que es lo que estamos esperando, es decir, que tiene el tamaño y extensión requeridos y no hay errores.

Validar si hay errores

Antes de comenzar, obviamente tenemos que comprobar si ha habido algún error durante la subida del archivo. Comencemos controlando este aspecto en nuestro archivo cargar.php:

<?php

$error = $_FILES['archivo']['error'];

if ($error === UPLOAD_ERR_OK) {
	// Lógica para guardar el archivo
} else {
	if ($error === UPLOAD_ERR_INI_SIZE || $error === UPLOAD_ERR_FORM_SIZE) {
		echo 'El tamaño del archivo sobrepasa el máximo permitido';
	} elseif ($error === UPLOAD_ERR_PARTIAL || $error === UPLOAD_ERR_NO_FILE) {
		echo 'El archivo no ha podido subirse correctamente';
	} elseif ($error === UPLOAD_ERR_NO_TMP_DIR) {
		echo 'No existe la carpeta temporal, contacta con el proveedor del hosting';
	} elseif ($error === UPLOAD_ERR_CANT_WRITE || $error === UPLOAD_ERR_EXTENSION) {
		echo 'Ha ocurrido un error durante la subida del archivo';
	}
}

?>

Nos hemos guardado el error en la variable $error para no estar constantemente escribiendo $_FILES[‘archivo’][‘error’]. Con una estructura IF y ELSE comprobamos si el error es UPLOAD_ERR_OK (o también 0), lo cual mediante la tabla anterior hemos indicado que es que todo ha ido bien. Desarrollaremos lo que sucede aquí dentro ahora luego. Si el error es distinto a UPLOAD_ERR_OK, entonces vamos a mostrar, dependiendo del error, un mensaje u otro.

Nota
No hemos hablado aún de las constantes, por lo que quizá veamos un poco extraño el utilizar UPLOAD_ERR_OK de esa forma, sin el símbolo $ como una variable. Las constantes, que veremos en un futuro, son valores que pueden ser predefinidos por el lenguaje o también por el programador y equivalen a un valor que les hayamos dado (normalmente un int o un string). En este caso, la constante UPLOAD_ERR_OK equivale a 0 y la estamos utilizando para mejorar la legibilidad del programa. Si hubiéramos colocado un 0 en lugar de esa constante, todo hubiera sido igual.

Si el “error” es igual a UPLOAD_ERR_OK, es decir, no hay error, seguiremos adelante.

Validar la extensión

Podemos validar la extensión de los archivos de subida para, por ejemplo, asegurarnos de recibir imágenes del tipo que queramos. Es más, deberíamos validar siempre la extensión. ¿Por qué? Para evitar que se adjunten archivos potencialmente dañinos con extensiones .php, .dat, .asp, etc., que difícilmente tendrán razón de ser en la subida de archivos de nuestros formularios. Normalmente querremos, según lo que pretendamos, archivos de imágenes, PDFs, archivos de texto plano o archivos de texto de los principales programas de edición. Controlar la extensión es parte de la seguridad a la hora de añadir archivos desde un formulario. Vamos con el código:

<?php

$extensiones = array('image/jpg', 'image/jpeg', 'image/png');

$error = $_FILES['archivo']['error'];

if ($error === UPLOAD_ERR_OK) {
	if (in_array($_FILES['archivo']['type'], $extensiones)) {
		echo 'La extensión es correcta';
                // Continuar con la lógica de subida de archivos
	} else {
		echo 'La extensión es incorrecta. El archivo no ha podido subirse';
	}
} else {
	if ($error === UPLOAD_ERR_INI_SIZE || $error === UPLOAD_ERR_FORM_SIZE) {
		echo 'El tamaño del archivo sobrepasa el máximo permitido';
	} elseif ($error === UPLOAD_ERR_PARTIAL || $error === UPLOAD_ERR_NO_FILE) {
		echo 'El archivo no ha podido subirse correctamente';
	} elseif ($error === UPLOAD_ERR_NO_TMP_DIR) {
		echo 'No existe la carpeta temporal, contacta con el proveedor del hosting';
	} elseif ($error === UPLOAD_ERR_CANT_WRITE || $error === UPLOAD_ERR_EXTENSION) {
		echo 'Ha ocurrido un error durante la subida del archivo';
	}
}

?>

Vamos a limitar la subida de archivos a imágenes jpg, jpeg y png en este ejemplo, los principales tipos de imágenes más usados. Los hemos guardado, con el nombre de su tipo MIME, en el array $extensiones.

Posteriormente, hacemos la comprobación de si $_FILES[‘archivo’][‘type’], que es donde tenemos el tipo MIME del archivo subido, está dentro del array $extensiones utilizando la función in_array que vimos en un artículo anterior. Si está, entonces la extensión del archivo es correcta. Si no lo está, deberemos indicarle el error al usuario.

Validar el tamaño

De igual forma, vamos a validar que el tamaño es el adecuado:

<?php

$extensiones = array('image/jpg', 'image/jpeg', 'image/png');
$tamanyo_maximo = 1024 * 1024 * 4;

$error = $_FILES['archivo']['error'];

if ($error === UPLOAD_ERR_OK) {
	if (in_array($_FILES['archivo']['type'], $extensiones)) {
		if ($_FILES['archivo']['size'] <= $tamanyo_maximo) {
			echo 'Extensión y tamaño correctos';
			// Continuar con la lógica de subida de archivos
		} else {
			echo 'El archivo excede el tamaño máximo de ' . $tamanyo_maximo . ' bytes';
		}
	} else {
		echo 'La extensión es incorrecta. El archivo no ha podido subirse';
	}
} else {
	if ($error === UPLOAD_ERR_INI_SIZE || $error === UPLOAD_ERR_FORM_SIZE) {
		echo 'El tamaño del archivo sobrepasa el máximo permitido';
	} elseif ($error === UPLOAD_ERR_PARTIAL || $error === UPLOAD_ERR_NO_FILE) {
		echo 'El archivo no ha podido subirse correctamente';
	} elseif ($error === UPLOAD_ERR_NO_TMP_DIR) {
		echo 'No existe la carpeta temporal, contacta con el proveedor del hosting';
	} elseif ($error === UPLOAD_ERR_CANT_WRITE || $error === UPLOAD_ERR_EXTENSION) {
		echo 'Ha ocurrido un error durante la subida del archivo';
	}
}

?>

No hay mucho secreto. Hemos creado una variable $tamanyo_maximo en la cual hemos guardado el tamaño máximo (1024 * 1024 * 4, es decir, 4194304). Si $_FILES[‘archivo’][‘size’] es mayor, entonces debemos impedir la subida del archivo y mostrar otro error. En caso contrario, sigamos avanzando.

Guardando finalmente nuestro archivo

Pasamos ya, por fin, a guardar el archivo en la carpeta donde queremos que resida finalmente:

<?php

$extensiones = array('image/jpg', 'image/jpeg', 'image/png');
$tamanyo_maximo = 1024 * 1024 * 4;

$error = $_FILES['archivo']['error'];

if ($error === UPLOAD_ERR_OK) {
	if (in_array($_FILES['archivo']['type'], $extensiones)) {
		if ($_FILES['archivo']['size'] <= $tamanyo_maximo) {
			$directorio = 'C:/xampp/htdocs/archivos/';
			$archivo_subido = $directorio . basename($_FILES['archivo']['name']);

			if (move_uploaded_file($_FILES['archivo']['tmp_name'], $archivo_subido)) {
				echo 'Se ha subido el archivo correctamente';
			} else {
				echo 'Ha ocurrido un error mientras se movía el archivo';
			}
		} else {
			echo 'El archivo excede el tamaño máximo de ' . $tamanyo_maximo . ' bytes';
		}
	} else {
		echo 'La extensión es incorrecta. El archivo no ha podido subirse';
	}
} else {
	if ($error === UPLOAD_ERR_INI_SIZE || $error === UPLOAD_ERR_FORM_SIZE) {
		echo 'El tamaño del archivo sobrepasa el máximo permitido';
	} elseif ($error === UPLOAD_ERR_PARTIAL || $error === UPLOAD_ERR_NO_FILE) {
		echo 'El archivo no ha podido subirse correctamente';
	} elseif ($error === UPLOAD_ERR_NO_TMP_DIR) {
		echo 'No existe la carpeta temporal, contacta con el proveedor del hosting';
	} elseif ($error === UPLOAD_ERR_CANT_WRITE || $error === UPLOAD_ERR_EXTENSION) {
		echo 'Ha ocurrido un error durante la subida del archivo';
	}
}

?>

Analicemos el nuevo código introducido. Una vez ya hemos comprobado que no ha habido errores, que tiene la extensión correcta y el tamaño adecuados, entonces tenemos que definirle la ruta/carpeta donde se va a guardar el archivo. Ésto dependerá del servidor y su estructura de carpetas, obviamente, así que en cada caso será distinto.

En el ejemplo, pongamos que la carpeta donde queremos guardar estas imágenes es C:/xampp/htdocs/archivos/ y nos la guardamos en la variable $directorio. A continuación, tenemos que indicarle la ruta completa y, para ello, concatenamos el directorio y el nombre del archivo. Queremos guardarlo con el mismo nombre del archivo subido (podríamos definir cualquiera, pero cuidado con sobreescribir los que ya hay), así que utilizamos la función basename que nos dejará el nombre del archivo sin la extensión y guardamos la ruta completa en $archivo_subido.

La función move_uploaded_file

Esta función será, finalmente, la que nos permita añadir archivos desde un formulario. Como su nombre en inglés nos indica, “mueve los archivos subidos”, es decir, cogerá el archivo que está en la carpeta temporal y sobre el que podemos acceder desde $_FILES[‘archivo’][‘tmp_name’] y lo moverá a la ruta que le hemos definido en $archivo_subido.

Lo colocamos dentro de un IF porque, si todo ha ido bien, nos devolverá un valor true pero, si ha habido algún error moviendo el archivo, nos devolverá false y podremos mostrarle al usuario que ha habido un error.

Ahora, vamos en nuestro ordenador a la carpeta que hemos indicado (C:/xampp/htdocs/archivos/ en este ejemplo) y vemos que sí, en efecto, allí hay ahora un archivo que es el que hemos subido. Desde nuestro navegador podremos acceder a este archivo desde esta misma ruta (en un hosting definitivo, accederíamos mediante la URL correspondiente que llevara a la carpeta donde se han guardado los archivos subidos).

Resumen

Pese a todos los pasos a seguir, subir archivos no requiere de grandes complicaciones. Únicamente debemos tener en cuenta:

  • crear un formulario acorde a la subida, con el enctype correspondiente y los <input> para subida de archivos, tipo file
  • validar, mediante el array $_FILES, que no haya habido errores
  • validar que la extensión y el tamaño sean los que nosotros hemos querido
  • definir la ruta/carpeta de destino y mover el archivo temporal a su ubicación definitiva

Pueden ser muchos pasos, pero podemos crearnos una función de subida de archivos a la que le pasemos como argumentos, por ejemplo, el name utilizado en el <input> correspondiente, las extensiones permitidas, el tamaño máximo y la ruta donde se guardará el archivo, y todo lo demás es idéntico.

Ahora que ya sabemos añadir archivos desde un formulario hemos terminado los temas básicos en cuanto a formularios y podemos volver a practicar con ellos.

Deja una respuesta