Upload con php y Apache

De ChuWiki

A veces queremos hacer una aplicación PHP que permita al usuario subir ficheros al servidor (imágenes, documentos, etc). Vamos a ver aquí la configuración necesaria para ello y un ejemplo tonto de cómo hacerlo.


Configuración de PHP[editar]

PHP suele estar configurado por defecto para permitir la subida de ficheros al servidor. De todas formas, no está de más comprobarlo para estar seguros y para saber dónde está esa configuración. Editamos el fichero de configuración php.ini, que en ubuntu está por defecto en /etc/php5/apache2/php.ini. Ahí buscamos estas líneas

;;;;;;;;;;;;;;;;
; File Uploads ;
;;;;;;;;;;;;;;;;

; Whether to allow HTTP file uploads.
file_uploads = On

; Temporary directory for HTTP uploaded files (will use system default if not
; specified).
;upload_tmp_dir =

; Maximum allowed size for uploaded files.
upload_max_filesize = 2M

En él, podemos ver tres variables:

  • file_uploads que debe estar a On para permitir la subida de ficheros.
  • upload_tmp_dir que está comentado. Esta variable indica un directorio donde temporalmente se subirán los ficheros hasta que nuestro código php los trate. Si está comentado, como en este caso, se usará el directorio temporal por defecto del sistema. En ubuntu es /tmp.
  • upload_max_filesize con el tamaño máximo del fichero de subida.

Al subir el fichero, este se guarda temporalmente en el directorio upload_tmp_dir y se le da un nombre "raro" que asegure que no existe ya. El fichero no se encontrará ahí con el mismo nombre que tenga el cliente que lo ha subido, sino con un nombre temporal.

Aunque está configurado por defecto a true desde PHP 4.0.3, en versiones anteriores debemos asegurarnos que la variable track_vars está a true. Si esta variable no está a true, no tendremos forma de ver desde nuestro código PHP dónde se ha subido el fichero ni con qué nombre.


El código HTML para el upload de fichero[editar]

En una página HTML tenemos que poner algo que permita al usuario elegir un fichero y subirlo. Para ello, debemos crear un formulario HTML con lo siguiente:

<html>
   <head>
      <title>subir fichero</title>
   </head>
   <body>
      <form action="sube.php" method="post" enctype="multipart/form-data">
         <input name="userfile" type="file">
         <input type="submit" value="Subir">
      </form>
   </body>
</html>

Este sería el formulario mínimo, aunque existen más opciones que podemos poner (veremos alguna más adelante).

En el formulario debemos usar método "post" y debemos añadir enctype="multipart/form-data", de esta forma permitiremos que el contenido del fichero circule con la petición http. El action será nuestra página .php encargada de tratar el fichero subido al servidor.

Ponemos un campo de tipo "file". Esto hará que el navegador muestre un campo de texto con el típico botón de "examinar", que permitirá al usuario elegir un fichero. Y finalmente, el botón de "subir", que es el que se encargará de ejecutar la acción.


El código PHP para el upload[editar]

En el código PHP tenemos que hacer un par de cosas sencillas, aunque por seguridad debemos complicarlo un poco.

En PHP 4.1.0 y superiores, tenemos la variable $_FILES, que nos da los datos del fichero que se ha subido (path, nombre temporal que se le ha dado, etc). En versiones anteriores, la variable es $HTTP_POST_FILES. En el primer caso, tenemos las siguientes opciones


  • $_FILES['userfile']['name'] : Nombre original del fichero en el ordenador del cliente.
  • $_FILES['userfile']['type'] : El MimeType del fichero, por ejemplo, "image/jpg". Este tipo lo decide el navegador, por lo que no estaría de más no fiarnos demasiado en nuestro código PHP.
  • $_FILES['userfile']['size'] : El tamaño del fichero en bytes.
  • $_FILES['userfile']['tmp_name'] : El nombre temporal que se le ha dado al fichero en nuestro servidor.
  • $_FILES['userfile']['error'] : Código de error en el upload, si ha habido error (disponible a partir de PHP 4.2.0)

Además de esto, en PHP tenemos la función move_uploadad_file($ficheroTemporal, $ficheroDefinitivo) que mueve el fichero desde su ubicacion temporal hasta la que nosotros decidamos como definitiva, con su nombre definitivo.

Un código PHP sencillo para hacer todo esto, puede ser el siguiente:

<?
$nombre_archivo = $_FILES['userfile']['name'];
$directorio_definitivo = "/var/www/upload/";
if (move_uploaded_file($_FILES['userfile']['tmp_name'], $directorio_definitivo.$nombre_archivo)){
       echo "El archivo ha sido cargado correctamente.";
    }else{
       echo "Ocurrió algún error al subir el fichero. No pudo guardarse.";
    }
?> 

Símplemente recogemos el nombre del archivo, elegimos el directorio donde queremos guardarlo y movemos el fichero temporal a su sitio. Ten en cuenta que el servidor apache debe tener permisos de escritura en el directorio elegido. En el caso de ubuntu y para este ejemplo, he creado un directorio upload en /var/www y he puesto al usuario www-data (que es el que usa ubuntu para arrancar apache)

$ cd /var/tmp
$ sudo mkdir upload
$ sudo chown www-data upload

También hay que tener en cuenta que este código se ha hecho muy simple para ver qué es lo que hay que hacer. En un código más serio habría que comprobar y protegerse sobre ciertas cosas.

Cosas a revisar para protegerse[editar]

Por un lado, en el código html, podemos poner restricción de tamaño, poniendo delante del campo tipo "file" un campo "hidden" con el tamaño máximo, de esta manera

...
<input type="hidden" name="MAX_FILE_SIZE" value="1000">
<input name="userfile" type="file">
...

sin embargo, esta protección depende del navegador y es sencillo incluso saltársela. Por ello, es mejor hacer también comprobaciones en el código PHP.

Dentro del código PHP deberíamos revisar (todo ello con $_FILES):

  • Ver que no ha ocurrido ningún error en la subida.
  • Ver que el tipo de fichero es el que esperamos.
  • Ver que el tamaño no supera el máximo permitido.


Enlaces[editar]