Lección 9: Ficheros

Subir un archivo

Un fichero es un elemento en binario que no es ni un número o ni un texto: imagen, video, música, doc, iso… Nosotros somos incapaces de leerlo a no ser que tengamos un ojo biónico y un chip en el cerebro. Si careces de estos dos requisitos solo podrás subirlo, por medio de un formulario, y almacenarlo en una carpeta.

Se debe tratar de una forma especial. Necesitaremos usar siempre el method POST y añadir enctype=”multipart/form-data”. Por último usar el input de tipo archivo (file).

<!-- Formulario -->
<form method="post" enctype="multipart/form-data">
    <p>
        <!-- Campo imagen -->
        <input type="file" name="fichero_usuario">
    </p>
    <p>
        <!-- Botón submit -->
        <input type="submit" value="Enviar">
    </p>
</form>

Cuando nuestro formulario sea enviado el archivo estará almacenado en una variable llamada $_FILES. La cual es un array con toda la información que vas a necesitar.

Nombre Ejemplo de contenido Descripción
$_FILES[‘fichero_usuario’][‘name’] ‘foto_en_la_playa.jpg’ Nombre del archivo
$_FILES[‘fichero_usuario’][‘type’] ‘image/png’ MIME (formato del archivo)
$_FILES[‘fichero_usuario’][‘size’] 3232424 Tamaño en bytes (5MB -> 5 x 1024 x 1024 bytes)
$_FILES[‘fichero_usuario’][‘error’] 0 Código de error. El 0 es que todo ha ido bien.
$_FILES[‘fichero_usuario’][‘tmp_name’] 213 Nombre temporal

Ahora solo tendremos que moverlo de la carpeta temporal a la definitiva, usando el método move_uploaded_file().

move_uploaded_file($_FILES['fichero_usuario']['tmp_name'], $fichero_subido);

Aquí puedes ver un ejemplo completo.

<html>
    <body>
        <?php
            //======================================================================
            // PROCESAR IMAGEN 
            //======================================================================
            // Comprobamos si nos llega los datos por POST
            if ($_SERVER['REQUEST_METHOD'] == 'POST') {
                // Definir directorio donde se guardará
                $dir_subida = './subidos/';
                // Definir la ruta final del archivo
                $fichero_subido = $dir_subida . basename($_FILES['fichero_usuario']['name']);
                // Mueve el archivo de la carpeta temporal a la ruta definida
                if (move_uploaded_file($_FILES['fichero_usuario']['tmp_name'], $fichero_subido)) {
                    // Mensaje de confirmación donde todo ha ido bien
                    echo '<p>Se subió perfectamente.</p>';
                    // Muestra la imagen que acaba de ser subida
                    echo '<p><img width="500" src="' . $fichero_subido . '"></p>';
                } else {
                    // Mensaje de error: ¿Límite de tamaño? ¿Ataque?
                    echo '<p>¡Ups! Algo ha pasado.</p>';
                }
            }
        ?>
        <!-- Formulario -->
        <form method="post" enctype="multipart/form-data">
            <p>
                <!-- Campo imagen -->
                <input type="file" name="fichero_usuario">
            </p>
            <p>
                <!-- Botón submit -->
                <input type="submit" value="Enviar">
            </p>
        </form>
    </body>
</html>

7-1 7-2

Multiarchivo (subir varios archivos)

Es posible subir varios archivos bajo el mismo nombre. Solo habrá que añadir unos corchetes ([]) después del name como si fuera un array.

<!-- Formulario -->
<form method="post" enctype="multipart/form-data">
    <p>
        <!-- Campos de imágenes -->
        <input type="file" name="imagen[]">
        <input type="file" name="imagen[]">
        <input type="file" name="imagen[]">
    </p>
    <p>
        <!-- Botón submit -->
        <input type="submit" value="Enviar">
    </p>
</form>

Al recibir los datos tendremos que iterar la variable, igual que un array. Es recomendable comprobar en cada caso que el archivo se ha subido correctamente.

// Comprobamos si nos llega los datos por POST
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
    // Iteramos todos los archivos
    foreach ($_FILES["imagen"]["error"] as $posicion => $error) {
        // Comprobamos si se ha subido correctamente
        if ($error == UPLOAD_ERR_OK) {
            // Definir directorio donde se guardará
            $dir_subida = './subidos/';
            // Definir la ruta final del archivo
            $fichero_subido = $dir_subida . basename($_FILES['imagen']['name'][$posicion]);
            // Mueve el archivo de la carpeta temporal a la ruta definida
            if (move_uploaded_file($_FILES['imagen']['tmp_name'][$posicion], $fichero_subido)) {
                // Mensaje de confirmación donde todo ha ido bien
                echo '<p>Se subió perfectamente' . $_FILES['imagen']['name'][$posicion] . '.</p>';
                // Muestra la imagen que acaba de ser subida
                echo '<p><img width="500" src="' . $fichero_subido . '"></p>';
            } else {
                // Mensaje de error: ¿Límite de tamaño? ¿Ataque?
                echo '<p>¡Ups! Algo ha pasado.</p>';
            }
        }
    }
}

Junto quedaría de la siguiente forma.

<html>
    <body>
        <?php
            //======================================================================
            // PROCESAR IMAGENES 
            //======================================================================
            // Comprobamos si nos llega los datos por POST
            if ($_SERVER['REQUEST_METHOD'] == 'POST') {
                // Iteramos todos los archivos
                foreach ($_FILES["imagen"]["error"] as $posicion => $error) {
                    // Comprobamos si se ha subido correctamente
                    if ($error == UPLOAD_ERR_OK) {
                        // Definir directorio donde se guardará
                        $dir_subida = './subidos/';
                        // Definir la ruta final del archivo
                        $fichero_subido = $dir_subida . basename($_FILES['imagen']['name'][$posicion]);
                        // Mueve el archivo de la carpeta temporal a la ruta definida
                        if (move_uploaded_file($_FILES['imagen']['tmp_name'][$posicion], $fichero_subido)) {
                            // Mensaje de confirmación donde todo ha ido bien
                            echo '<p>Se subió perfectamente' . $_FILES['imagen']['name'][$posicion] . '.</p>';
                            // Muestra la imagen que acaba de ser subida
                            echo '<p><img width="500" src="' . $fichero_subido . '"></p>';
                        } else {
                            // Mensaje de error: ¿Límite de tamaño? ¿Ataque?
                            echo '<p>¡Ups! Algo ha pasado.</p>';
                        }
                    }
                }
            }
        ?>
        <!-- Formulario -->
        <form method="post" enctype="multipart/form-data">
            <p>
                <!-- Campos de imágenes -->
                <input type="file" name="imagen[]">
                <input type="file" name="imagen[]">
                <input type="file" name="imagen[]">
            </p>
            <p>
                <!-- Botón submit -->
                <input type="submit" value="Enviar">
            </p>
        </form>
    </body>
</html>

Borrar archivos

Para eliminar un archivo debemos usar el método unlink.

unlink('archivo');

Tan solo hay que dar la ruta que deseamos.

unlink('subidos/coche_rojo.jpg');

Evitar que se sobrescriba

¿Qué pasa si subimos dos archivos con el mismo nombre? Pues que el anterior desaparecería, se sobrescribiría. Un truco para solucionarlo es usando sha1_file().

echo sha1_file('foto.jpg');
// 2d68e69c476166978146c4f8e523ba8f87acbbc3

Nos devuelve el hash de un archivo. O, lo que es lo mismo, un texto alfanumérico generado por el algoritmo Hash el cual nos garantiza que el archivo es único.

Si tuvieramos un archivo llamado reloj.jpg.

$fichero_subido = $dir_subida . sha1_file($_FILES['fichero_usuario']['tmp_name']) . basename($_FILES['fichero_usuario']['name']);
echo $fichero_subido;
// subidos/2d68e69c476166978146c4f8e523ba8f87acbbc3reloj.jpg

Por muchos archivos reloj.jpg que suban nunca se sobrescribirán.

Tamaño máximo

Si quieres limitar el tamaño de todos los archivos puedes hacerlo añadiendo un input especial.

<input type="hidden" name="MAX_FILE_SIZE" value="20000" />

El value se mide en bytes.

Si un archivo supera nuestra frontera dará un error, pero nunca llegará a subirse. Evitando que nos suban archivos enormes para luego avisar al usuario que ha superado el tamaño máximo.

Igualmente valida el tamaño con PHP, es fácil manipular el límite dentro del navegador. Recuerda: nunca te fíes del usuario.

7-3

Procesamiento de imágenes

PHP no esta solamente limitado a la generación de HTML y mover archivos, también puede procesar imágenes. Existen una multitud de posibilidades.

  • Redimensionar (utilizado para crear miniaturas).
  • Recortar.
  • Aplicar filtros de color.
  • Crear imágenes (como suena).
  • Añadir marcas de agua.
  • Cambiar de formato.

Dispone de diversas herramientas para manipular formatos tan conocidos como: JPEG, GIF, PNG y WebP (entre otros). Puedes ver más en la documentación.