Aprendiendo PHP, parte 19: Subir archivos al servidor

Los sitios web más modernos, como por ejemplo las redes sociales están formados en gran parte por material que van subiendo sus usuarios como fotos, música, vídeos, etc. Para ello la aplicación web debe darle al visitante la posibilidad de subir estos archivos. Hasta ahora hemos visto como enviar formularios mediante campos de ingreso de texto o campos que nos permiten seleccionar entre un grupos de opciones. En esta ocasión veremos el elemento que nos está faltando: input file y como recibe la información que ha enviado éste en el servidor.

Para realizar este ejemplo vamos a crear un nuevo proyecto en nuestro localhost, yo en mi caso lo llamaré subir_archivos.

El mismo tendrá un archivo, index.php y una carpeta llamada archivos. Ahora vamos a editar el archivo index.php con el siguiente código:

<!DOCTYPE html>
<html>
 <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title> Subir archivos </title>
 </head>
 <body>
    <form method="post" action="index.php" enctype="multipart/form-data">
       <label> Archivo </label>
       <input type="file" name="archivo" required="required" />
       <input type="submit" value="Subir" />
    </form>
 </body>
</html>

Bien, un código muy sencillo, sin embargo vamos a analizar dos cosas antes de seguir. En primer lugar usamos el elemento input file, el cual nos permite cargar un archivo:

<input type="text" name="archivo" required="required" />

Además, otra cosa a tener en cuenta es que al formulario le agregamos un atributo enctype, con el valor «multipart/form-data». Esto es necesario para que el formulario sea capaz de enviar archivos al servidor:

enctype="multipart/form-data"

Bien, ahora para recuperar un archivo enviado desde el navegador al servidor debemos hacerlo mediante la variable $_FILES, que es un array en donde el índice corresponde al name del campo con el valor que se envió, en nuestro caso con el nombre ‘archivo’. A su vez cada posición será un array asociativo con los índices:

  • name: El nombre del archivo con el que se subió al servidor, por ejemplo manzana.jpg.
  • type: Qué tipo de archivo, por ejemplo si es un .jpg será image/jpeg.
  • tmp_name: El archivo al ser subido se guardará automáticamente a un directorio temporal, el valor de éste es justamente la ruta al mismo.
  • error: Nos devolverá un error si se ha producido alguno al intentar recuperar este archivo desde el servidor, de lo contrario devolverá 0.
  • size: Es el tamaño en bytes del archivo.
Ahora para recuperar el archivo desde el servidor y guardarlo en la carpeta archivos haremos lo siguiente:
<?php
   $archivo = (isset($_FILES['archivo'])) ? $_FILES['archivo'] : null;
   if ($archivo) {
      $ruta_destino_archivo = "archivos/{$archivo['name']}";
      $archivo_ok = move_uploaded_file($archivo['tmp_name'], $ruta_destino_archivo);
   }
?>

Primero guardamos en la variable $archivo el resultado si se ha enviado o no un archivo:

$archivo = (isset($_FILES['archivo'])) ? $_FILES['archivo'] : null;

Luego preguntamos con un if el resultado de la variable anterior, osea si hay algún archivo que el usuario ha intentado subir.

De ser así creamos una variable donde guardamos la ruta en donde queremos guardar el archivo:

$ruta_destino_archivo = "archivos/{$archivo['name']}";

En nuestro caso, dentro de la carpeta archivos con el mismo nombre con la que lo envió el usuario.

Y finalmente usamos la función move_uploaded_file(), ésta recibirá dos parámetros, la ruta donde se encuentra el archivo actualmente y adónde lo queremos mover:

$archivo_ok = move_uploaded_file($archivo['tmp_name'], $ruta_destino_archivo);

Osea, la ruta temporal en donde se guarda automáticamente el archivo, y la nueva ruta, la que queremos nosotros.

Esta función move_uploaded_file() devolverá true o false, dependiendo de si se pudo o no mover el archivo, ese resultado lo guardamos en la variable $archivo_ok.

El código finalmente quedaría así:

<?php
$archivo = (isset($_FILES['archivo'])) ? $_FILES['archivo'] : null;
if ($archivo) {
   $ruta_destino_archivo = "archivos/{$archivo['name']}";
   $archivo_ok = move_uploaded_file($archivo['tmp_name'], $ruta_destino_archivo);
}
?>
<!DOCTYPE html>
<html>
 <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title> Subir archivos </title>
 </head>
 <body>
    <?php if (isset($archivo)): ?>
       <?php if ($archivo_ok): ?>
          <strong> El archivo ha sido subido correctamente. </strong>
       <?php else: ?>
          <span style="color: #f00;"> Error al intentar subir el archivo. </span>
       <?php endif; ?>
    <?php endif; ?>
    <form method="post" action="index.php" enctype="multipart/form-data">
       <label> Archivo </label>
       <input type="file" name="archivo" required="required" />
       <input type="submit" value="Subir" />
    </form>
 </body>
</html>

Al código html finalmente agregamos un condicional preguntando si existe un archivo que ha intentado subir el usuario y de ser así si se ha podido subir:

<?php if (isset($archivo)): ?>
    <?php if ($archivo_ok): ?>
       <strong> El archivo ha sido subido correctamente. </strong>
    <?php else: ?>
       <span style="color: #f00;"> Error al intentar subir el archivo. </span>
    <?php endif; ?>
<?php endif; ?>

Ahora, una de las cosas más importantes que tenemos que tener en cuenta es que cuando le damos la posibilidad al usuario de subir archivos, debemos tener un filtro de los tipos que éste puede subir. Por ejemplo supongamos que nuestra aplicación sólo permite subir imágenes con extensión jpg, jpeg, gif o png, antes de mover el archivo a nuestra carpeta archivos, debemos preguntar si éste cumple con las condiciones. Así que vamos a modificar la cabecera:

<?php
   $archivo = (isset($_FILES['archivo'])) ? $_FILES['archivo'] : null;
   if ($archivo) {
      $ruta_destino_archivo = "archivos/{$archivo['name']}";
      $archivo_ok = move_uploaded_file($archivo['tmp_name'], $ruta_destino_archivo);
   }
?>

Por esto:

<?php
   $archivo = (isset($_FILES['archivo'])) ? $_FILES['archivo'] : null;
   if ($archivo) {
      $extension = pathinfo($archivo['name'], PATHINFO_EXTENSION);
      $extension = strtolower($extension);
      $extension_correcta = ($extension == 'jpg' or $extension == 'jpeg' or $extension == 'gif' or $extension == 'png');
     if ($extension_correcta) {
        $ruta_destino_archivo = "archivos/{$archivo['name']}";
        $archivo_ok = move_uploaded_file($archivo['tmp_name'], $ruta_destino_archivo);
     }
  }
?>

Como se ve en el ejemplo mediante las líneas:

$extension = pathinfo($archivo['name'], PATHINFO_EXTENSION);
$extension = strtolower($extension);

Recuperamos la extensión del archivo mediante a su name, osea el nombre del archivo, que será algo como manzana.jpg, y nosotros obtenemos dentro de la variable $extension valga la redundancia, la extensión del archivo, por ejemplo jpg. Luego pisamos esta variable con el mismo valor, pero todo en minúsculas, esto se debe a que es muy común en Windows que haya archivo con la extensión en mayúscula.

Luego preguntamos si la extensión es correcta y de serlo intentamos mover el archivo:

if ($extension_correcta) {
   $ruta_destino_archivo = "archivos/{$archivo['name']}";
   $archivo_ok = move_uploaded_file($archivo['tmp_name'], $ruta_destino_archivo);
}

Y finalmente para mostrar por pantalla el resultado del mismo, vamos a modificar:

<?php if (isset($archivo)): ?>
    <?php if ($archivo_ok): ?>
       <strong> El archivo ha sido subido correctamente. </strong>
    <?php else: ?>
       <span style="color: #f00;"> Error al intentar subir el archivo. </span>
    <?php endif; ?>
<?php endif; ?>

Por esto:

<?php if (isset($archivo)): ?>
   <?php if (!$extension_correcta): ?>
      <span style="color: #f00;"> La extensión es incorrecta, el archivo debe ser jpg, jpeg, gif o png. </span> 
   <?php elseif (!$archivo_ok): ?>
      <span style="color: #f00;"> Error al intentar subir el archivo. </span>
   <?php else: ?>
      <strong> El archivo ha sido subido correctamente. </strong>
      <br />
      <img src="archivos/<?php echo $archivo['name'] ?>" alt="" />
   <?php endif ?>
 <?php endif; ?>

Primero preguntamos si la extensión es incorrecta y de serlo mostramos por pantalla el mensaje con el error:

<?php if (!$extension_correcta): ?>
      <span style="color: #f00;"> La extensión es incorrecta, el archivo debe ser jpg, jpeg, gif o png. </span>

Luego si el hubo algún error al intentar subir el archivo:

<?php elseif (!$archivo_ok): ?>
      <span style="color: #f00;"> Error al intentar subir el archivo. </span>

Y de que estas dos condiciones no se cumpla ninguna, osea que el archivo tiene una extensión correcta y se subió bien mostramos el mensaje y la imagen que acaba de subir el usuario:

<?php else: ?>
   <strong> El archivo ha sido subido correctamente. </strong>
   <br />
   <img src="archivos/<?php echo $archivo['name'] ?>" alt="" />
<?php endif ?>

El código entonces finalmente quedaría así:

<?php
$archivo = (isset($_FILES['archivo'])) ? $_FILES['archivo'] : null;
if ($archivo) {
   $extension = pathinfo($archivo['name'], PATHINFO_EXTENSION);
   $extension = strtolower($extension);
   $extension_correcta = ($extension == 'jpg' or $extension == 'jpeg' or $extension == 'gif' or $extension == 'png');
   if ($extension_correcta) {
      $ruta_destino_archivo = "archivos/{$archivo['name']}";
      $archivo_ok = move_uploaded_file($archivo['tmp_name'], $ruta_destino_archivo);
   }
}
?>
<!DOCTYPE html>
<html>
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <title> Subir archivos </title>
 </head>
 <body>  
    <?php if (isset($archivo)): ?>
       <?php if (!$extension_correcta): ?>
         <span style="color: #f00;"> La extensión es incorrecta, el archivo debe ser jpg, jpeg, gif o png. </span> 
       <?php elseif (!$archivo_ok): ?>
         <span style="color: #f00;"> Error al intentar subir el archivo. </span>
      <?php else: ?>
         <strong> El archivo ha sido subido correctamente. </strong>
         <br />
         <img src="archivos/<?php echo $archivo['name'] ?>" alt="" />
      <?php endif ?>
   <?php endif; ?>
   <form method="post" action="index.php" enctype="multipart/form-data">
      <label> Archivo </label>
      <input type="file" name="archivo" required="required" />
      <input type="submit" value="Subir" />
   </form>
 </body>
</html>

 

Anterior: Aprendiendo PHP, parte 18: Validar formularios

Siguiente: Aprendiendo PHP, parte 20: Cookies

 


17 Respuestas a “Aprendiendo PHP, parte 19: Subir archivos al servidor”

        1. Jorge, por favor enviame el código a mi casilla de correo con el archivo adjunto, porque acá en el blog se complia.

          Mi dirección de email es: fernandoggaitan(gmail)

          Saludos.

  1. saludos, y si quiero guardar la ruta del archivo en la base de datos con tu codigo que debo hacer

  2. Hola que tal, una consulta:
    Como podria hacerlo para subir cualquier tipo de archivo a una base de datos en servidor web de prueba? . Espero entienda mi duda. Gracias.

  3. Hola ferchu disculpa mi ignorancia pero soy nuevo en esto de programacion copie tu codigo y me sale estos errores:

    Warning: move_uploaded_file(archivos/Desert.jpg) [function.move-uploaded-file]: failed to open stream: No such file or directory in C:\AppServ\www\archivos\index.php on line 9

    Warning: move_uploaded_file() [function.move-uploaded-file]: Unable to move ‘C:\Windows\TEMP\phpDF8C.tmp’ to ‘archivos/Desert.jpg’ in C:\AppServ\www\archivos\index.php on line 9

    no se que hice mal por si acaso utilizo appserv como servidor local
    apreciaria mucho tu ayuda GRACIAS

    1. Por favor verificá si estás pasando la ruta correctamente, porque el error me parece que viene por ahí.

      Saludos!

  4. buenas, disculpa esto funciona para usar a mongoDB como servidor?
    espero me pueda ayudar en eso, o brindarme donde puedo conseguir respecto a ese tema, gracias.

  5. Hola, me funciono perfectamente, ahora el problema que tengo es en el nombre del archivo. No se como hacer para que guarde los archivos con un nombre consecutivo, por ejemplo:

    imagen01.jpg, imagen02.jpg, imagen03.jpg, etc.

  6. Excelente post, quisiera saber si el código también aplica para subir archivos con extensión xml gracias.