Creando un área de cliente para portfolios con PHP y MySQL – Cuarta parte

En la parte 3, hemos hecho básicamente cosas que podrías encontrar en cualquier tutorial de PHP/MySQL. Ahora, sin embargo, llegamos a la parte que lo convierte en un “área de cliente”. En este tutorial cubriremos la sección “documentos compartidos” del área de cliente.

Compartir documentos de ida y vuelta entre los cliente puede ser una molestia real a través del correo electrónico y otras herramientas en línea para manejar esto pueden salir caras. Tener tu propia área de clientes para gestionar los documentos es una buena manera de combatir ambos problemas.

Para empezar, vamos a hacer una lluvia de ideas de lo que necesitaremos hacer en la página:

  • Debemos permitir que el cliente cargue documentos (texto, imágenes, documentos de Word, etc…)
  • Debe procesar el archivo cargado para determinar el tamaño, el tipo, y colocarlo en el área correcta. También debe ser lo suficientemente inteligente como para evitar ataques o archivos enormes que perjudicarían las cosas al final.
  • Debe mostrar cualquier documento ya cargado, y dejar que el cliente pueda verlo/descargarlo de nuevo.

Ahora que tenemos un poco de organización, empecemos.

Crear el archivo documents.php

Antes de entrar en ningún tipo de codificación real, vamos a crear un archivo para el área de documentos compartidos. Lo llamaré “documents.php” en este tutorial. Recordemos también determinar si el cliente está conectado, de lo contrario les redirigiremos a la pantalla de ingreso.

Esto se hizo también en la página de perfil (en la parte 3) por lo que basta con un poco de copiar/pegar de esta página, y para cualquier página que deseemos proteger.

<html>
<head>
<meta charset="ISO-8859-1" />
<title>Documentos</title>
</head>
<body>
<?php
session_start();
if(!isset($_SESSION['username'])){
 header('Location:login.php');
}
require('db.php');

En cuanto estemos más familiarizados con nuestro código, puede que no sea necesario mantener todos los comentarios (a pesar de que debe ser decentemente documentado). Esto ahorrará espacio en nuestro archivo.

El script de carga

Vamos a querer mantener todos los archivos cargados en una carpeta, preferentemente llamada “uploads”. Debido a nuestro objetivo en este script, sin embargo, vamos a tener que encontrar una manera de asociar a un cliente con sus propios archivos.

Para ello, vamos a crear una nueva tabla en nuestra base de datos y almacenar la ruta de acceso del archivo cargado junto con el mismo ID que nuestro cliente actual. Aquí vemos un desglose de cómo podemos trabajar:

  • El cliente ha iniciado sesión, con sus variables de sesión establecidas. A pesar de que nunca se pone el ID del cliente en las variables de la sesión, podemos encontrarlo fácilmente.
  • Cuando el cliente sube un archivo, nuestro script de carga hará una copia del archivo y lo guardaremos en una nueva ubicación. Pondremos tanto esta URL como el ID de cliente en una nueva tabla llamada “uploads”
  • Cada vez que el cliente carga un documento nuevo, tendrá el mismo número de identificación de referencia. Así que cuando llegue el momento de recuperar la información, podremos utilizar este número de ID para mostrar la información de los archivos.
  • Con bucle, podemos encontrar fácilmente todos los archivos del cliente, y con su enlace almacenado, podemos mostrar en consecuencia el enlace de descarga.

Vamos a comenzar este proceso. Tendremos que añadir más seguridad básica una vez tengamos un primer borrador de nuestro script listo.

Formulario para subir los archivos

Para el formulario de carga, he usado la forma básica del tutorial Subir archivo de Tizag. Debajo está la explicación de nuestro formulario, pero puedes revisar este tutorial en su totalidad para tener una visión más clara.

<form enctype="multipart/form-data" action="documents.php" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="2097152" />
Escoge el archivo a subir: <input name="uploadedfile" type="file" /><br />
<input type="submit" value="Subir archivo" />

He aquí una breve descripción de las partes importantes de la seguridad del código:

  • Enctype=”multipart/form-data” – Necesario para que el archivo PHP que crearemos funcione correctamente.
  • Action=”documents.php” – El nombre de la página PHP que crearemos en breve.
  • Method=”post” – Informa al navegador que queremos enviar la información al servidor mediante POST.
  • Input type=”hidden” name=”MA… – Establece el tamaño máximo permitido del archivo, en bytes, que se puede cargar. Este mecanismo de seguridad se omite fácilmente y nos mostrará una solución de copia de seguridad sólida en PHP. Hemos establecido el tamaño máximo de archivo e 2Mb en este ejemplo.
  • Input name=”uploadedfile” – Uploadedfile es cómo vamos a acceder al archivo en nuestro script PHP.

Crear una tabla en la base de datos para guardar nuestras URL’s

Antes de entrar a programar, vamos a necesitar crear una tabla en la base de datos que contenga dos cosas: Un número de identificación que corresponda a la ID del cliente y la dirección URL para el archivo guardado. Después de crearla, podremos almacenar la información en esta tabla, y luego usarla más tarde para crear el resto de nuestra área de documentos.

  1. Inicia sesión de phpMyAdmin, y entra en la base de datos ClientArea.
  2. Crea una nueva tabla, llámala “uploads”. Introduce dos campos y pulsa “IR”.Nueva tabla - Uploads
  3. Rellena la siguiente información a continuación. Especifica esto según tus propias necesidades:Campos - Uploads

Todo en la imagen de arriba debe ser igual, o al menos similar, dependiendo de los nombres de las celdas, a excepción de “Longitud/Valores”. Al poner la longitud de ID a 2, esto da lugar a 99 clientes. Si crees que vas a tener más durante la vida de este script, no dudes en cambiarlo a 3. Esto te permitirá almacenar hasta 999 clientes.

La longitud de la URL depende de dónde se van a guardar los archivos, y el nombre de nuestra carpeta. La longitud de caracteres tendrá que incluir el nombre de la carpeta de carga, el nombre del archivo, la extensión, y cualquier signo de puntuación. Un nombre de archivo de ejemplo sería “uploads/contentforwebsite.doc”, lo que daría 29 caracteres. Déle al usuario libertad de acción suficiente para la longitud del nombre de archivo, pero que no sea tan largo para que no se pueda hacer spam.

El script de carga

Continuamos siguiendo el tutorial de Tizag:

Cuando se ejecuta el archivo “documents.php”, existe el archivo subido en un área de almacenamiento temporal del servidor. Si el archivo no se mueve a una ubicación diferente será destruido. Para guardar nuestro archivo vamos a tener que hacer uso de la matriz asociativa $_FILES.

El array $_FILES es donde se guarda toda la información acerca de los archivos. Hay dos elementos de esta matriz que tendremos que entender para este ejemplo.

  • Uploadedfile – uploadedfile es la referencia que asignamos en nuestro formulario HTML. Vamos a necesitar esto para informarle a la matriz $_FILES con qué archivo queremos jugar.
  • $_FILES[‘uploadedfile’][‘name’] – Nombre que contiene la ruta original del archivo subido por el usuario.
  • $_FILES[‘uploadedfile’][‘tmp_name’] – tmp_name contiene la ruta de acceso al archivo temporal que reside en el servidor. El archivo debe existir en el servidor en un directorio temporal con un nombre temporal.

Ahora por fin podemos empezar a escribir un script de carga en PHP. Aquí empieza la parte en la que vamos a tener que conseguir el nombre del archivo temporal, elegir un nombre permanente, y un lugar para guardar el archivo.

Crea una carpeta llamada “uploads” en el directorio que contiene todos los demás archivos en el área de cliente. El script no lo hará por ti, y tienes que crearlo antes de que la secuencia de comandos se haya ejecutado nunca.

Agrega el siguiente código para la parte PHP de la página. Esto debe estar en “documents.php” en donde nos registramos si el usuario se ha autentificado.

// Dónde se va a colocar el archivo
 $target_path = "uploads/";
// Añade el nombre del archivo original a la ruta de destino.
// El resultado es "uploads/filename.extension"
 $target_path = $target_path.basename($_FILES['uploadedfile']['name']);

Básicamente hemos utilizado unas pocas líneas de código para crear una sola variable “$target_path”. Esta será la ruta donde queremos subir nuestro archivo temporal. Este será guardado permanentemente guardado aquí.

El código tomará dos parámetros: La ruta temporal actual ($_FILES[‘uploadedfile’][‘tmp_name’]) y la nueva ruta donde nos gustaría guardar el archivo ($target_path).

Estará contenido en una instancia IF/ELSE. Si la función se activa y el archivo se mueve, introducimos los datos en la base de datos y recargamos la página. De lo contrario, nos avisa de un error.

Vamos a cambiar la forma del IF/ELSE para satisfacer mejor nuestro objetivo. Un mensaje de error está muy bien, y podemos hacer más cosas si el archivo se ha movido con éxito. A continuación vamos a hacer esto:

  1. Introducir el ID del cliente y la dirección URL en nuestra nueva tabla de la base de datos.
  2. Devolver al cliente a la página documents.php. Visualizaremos los archivos más adelante en nuestro tutorial, y en esa actualización debe mostrar el nuevo archivo.

Primero vamos a insertar los datos en nuestra tabla mediante una consulta MySQL:

if(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {
 $result = "INSERT INTO uploads (id, url) VALUES ('".$_SESSION['client_ID']."', '$target_path')";
 $sql = mysql_query($result);
 header('Location:documents.php');
 } else {
 echo "Hubo un error al subir el archivo, por favor, ¡prueba de nuevo!<br />";
 }

Creamos una variable llamada “client_ID” y seleccionamos el ID de nuestro cliente de nuestra base de datos mediante una consulta MySQL. Hemos sido capaces de recuperar la información correcta al seleccionar el campo donde nuestro nombre de usuario era igual al nombre de usuario en nuestra variable de sesión. Además, ten en cuenta que que lo ponemos fuera del IF/ELSE para poder así utilizarlo más tarde.

Por último, se utilizó otra consulta MySQL para insertar el ID del cliente y la ruta del archivo en la base de datos dentro de la tabla “uploads”. Al final todo lo que tenemos que hacer es redirigir al usuario hacia la misma página.

Asegúrate de probarlo intentando subir un archivo. Para comprobar que funciona, mira primero en la base de datos y luego en la carpeta “uploads”.

Haciendo nuestro código más seguro

Para empezar, vamos a pensar en nuestra estructura. En esta parte sólo vamos a comprobar el tipo de archivo y el tamaño. Puede ser necesario que tengamos que usar otros métodos de seguridad.

Después de identificar qué queremos comprobar, tenemos que identificar el problema: Comprobamos el tamaño y el tipo del archivo, pero si hay algo mal con cualquiera de ellos, no podemos dejar que se escriban los datos. Para resolver este problema, vamos a usar otro IF/ELSE simple.

if((!$_FILES["uploadedfile"]["type"] == "image/gif")
 ||(!$_FILES["uploadedfile"]["type"] == "image/png")
 ||(!$_FILES["uploadedfile"]["type"] == "image/jpeg") // "jpeg" for Firefox
 ||(!$_FILES["uploadedfile"]["type"] == "image/pjpeg") // "jpeg" for IE
 ||(!$_FILES["uploadedfile"]["type"] == "application/msword")
 ||(!$_FILES["uploadedfile"]["type"] == "application/pdf")
 &&(!$_FILES["file"]["size"] < 2097152)){
 echo "El archivo no es del tipo o tamaño correcto. Debería ser un .gif, .png, .jpeg/.jpg, .doc, .docx, .pdf o menor de 2Mb.";
 echo "<br />";
 echo "Si tiene que enviarme un archivo diferente de estas especificaciones, no dude en contactarme por correo electrónico a josecarlosnietoramos@hotmail.com. Estas especificaciones son para la seguridad de la página web.<br />";
 } else { }

Antes de seguir, vamos a ver qué hicimos en este IF/ELSE. Para verlo más fácilmente, es de color naranja. En nuestra matriz $_FILES[‘uploadedfile’][‘type’] nos da nuestro tipo MIME para el tipo de archivo que hemos subido. Un tipo MIME es básicamente una manera de decirle al navegador qué tipo de archivos estamos tratando. Podemos obtener una lista de los tipos MIME disponibles en este enlace. Para comprobar si el tipo de archivo dado es igual al tipo MIME deseado (por ejemplo, “image/png”) podemos especificar qué archivos se desea incluir.

Ten en cuenta que hay un símbolo de exclamación (!) antes de cada comparación. Esto se debe a que estamos revisando para ver si no es de ese tipo MIME. Si no lo es, entonces el cliente ha subido el tipo equivocado de archivo. Además, “||” significa “OR” y “&&” quiere decir “AND”. Así que lo que estamos diciendo en este condicional es: “Si nuestro tipo de archivo no es igual a GIF, PNG, JPG, JPEG, PJPEG, MSWORD O PDF, Y no es del tamaño de archivo que necesitamos”, entonces han subido un archivo que rechazamos y nos mostrará ese mensaje. El mensaje está en azul para diferenciar el código.

Debemos tener en cuenta que esta no es la manera más eficiente de hacer esto, teniendo en cuenta el número de tipos de archivo que estamos permitiendo. Una mejor manera sería almacenar los tipos MIME en una matriz, y pasar por la matriz al comparar el archivo actual. Yo lo he hecho de esta manera por la sencillez. Sería buena práctica, e inteligente, para encontrar maneras de hacer el código más eficiente. En otros casos, sin embargo, si sólo quisiéramos incluir algunos tipos de archivos, esta forma de codificación estaría completamente bien.

A continuación, vamos a incluir la parte “ELSE”, que sólo va a ser nuestro código que inserta los datos de nuestros documentos en nuestra nueva tabla.

if((!$_FILES["uploadedfile"]["type"] == "image/gif")
 ||(!$_FILES["uploadedfile"]["type"] == "image/png")
 ||(!$_FILES["uploadedfile"]["type"] == "image/jpeg") // "jpeg" for Firefox
 ||(!$_FILES["uploadedfile"]["type"] == "image/pjpeg") // "jpeg" for IE
 ||(!$_FILES["uploadedfile"]["type"] == "application/msword")
 ||(!$_FILES["uploadedfile"]["type"] == "application/pdf")
 &&(!$_FILES["file"]["size"] < 2097152)){
 echo "El archivo no es del tipo o tamaño correcto. Debería ser un .gif, .png, .jpeg/.jpg, .doc, .docx, .pdf o menor de 2Mb.";
 echo "<br />";
 echo "Si tiene que enviarme un archivo diferente de estas especificaciones, no dude en contactarme por correo electrónico a josecarlosnietoramos@hotmail.com. Estas especificaciones son para la seguridad de la página web.<br />";
 } elseif(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {
 $result = "INSERT INTO uploads (id, url) VALUES ('".$_SESSION['client_ID']."', '$target_path')";
 $sql = mysql_query($result);
 header('Location:documents.php');
 } else {
 echo "Hubo un error al subir el archivo, por favor, ¡prueba de nuevo!<br />";
 }

Comprobando que se ha subido el archivo

Como vamos a cargar el código cada vez que el cliente accede a la página, no sería inteligente ejecutar el código para cargar el documento a menos que, efectivamente, el cliente trate de cargar un documento. Así, alrededor de nuestro código de carga podemos agregar una sencilla declaración IF que se asegure de que nuestra matriz asociativa $_FILES no está vacía. Si está vacía, se ignorará el código, y nos moveremos por el resto de la página.

if (!empty($_FILES)) {
 $target_path = $target_path.basename($_FILES['uploadedfile']['name']);
if((!$_FILES["uploadedfile"]["type"] == "image/gif")
 ||(!$_FILES["uploadedfile"]["type"] == "image/png")
 ||(!$_FILES["uploadedfile"]["type"] == "image/jpeg") // "jpeg" for Firefox
 ||(!$_FILES["uploadedfile"]["type"] == "image/pjpeg") // "jpeg" for IE
 ||(!$_FILES["uploadedfile"]["type"] == "application/msword")
 ||(!$_FILES["uploadedfile"]["type"] == "application/pdf")
 &&(!$_FILES["file"]["size"] < 2097152)){
 echo "El archivo no es del tipo o tamaño correcto. Debería ser un .gif, .png, .jpeg/.jpg, .doc, .docx, .pdf o menor de 2Mb.";
 echo "<br />";
 echo "Si tiene que enviarme un archivo diferente de estas especificaciones, no dude en contactarme por correo electrónico a josecarlosnietoramos@hotmail.com. Estas especificaciones son para la seguridad de la página web.<br />";
 } elseif(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {
 $result = "INSERT INTO uploads (id, url) VALUES ('".$_SESSION['client_ID']."', '$target_path')";
 $sql = mysql_query($result);
 header('Location:documents.php');
 } else {
 echo "Hubo un error al subir el archivo, por favor, ¡prueba de nuevo!<br />";
 }
 }

Visualizar los documentos del cliente

En los próximos pasos, vamos a mostrar todos los documentos cargados por el cliente, y permitirle descargarlos si fuese necesario.

Mostrar los archivos

Con el fin de mostrar los archivos, cada vez que cargue la página documents.php debe comprobar si hay algún archivo en la tabla “uploads” con nuestro ID de cliente. Si lo hay, podemos usar un bucle para mostrarlos todos. Además, podemos usar algo de HTML básico para dar formato a este.

//Mostrando todos los archivos
$getDocuments = mysql_query("SELECT * FROM uploads WHERE ID='".$_SESSION['client_ID']."'");
while($row = mysql_fetch_array($getDocuments, MYSQL_ASSOC)) {
echo "URL: ".$row['url'];
}

Lo que hacemos primero es utilizar una consulta MySQL para seleccionar todos los archivos de la carpeta donde “ID” sea igual a nuestra variable $client_id (definido anteriormente en el código). Luego, nos metimos en un bucle While. Recordamos, el mysql_fetch_array crea una matriz asociativa para poder trabajar con nuestra variable $getDocuments. Al asignar esta función a la variable $row, se puso cada fila de esta matriz en la variable a medida que avanzamos a través de ese bucle While. En cada iteracción a través del bucle, simplemente mostramos la dirección URL del archivo.

Descarga de archivos.

Para descargar el archivo, sólo tenemos que vincularlo a la fuente original. Tenemos la fuente original en el nombre de ruta, así que si mezclamos un poco de PHP y HTML podremos crear un enlace de descarga con éxito.

<a href="document.doc">document.doc</a>

Arriba está la forma en HTML plano para crear un enlace de descarga. Vamos a ver cómo podemos integrar PHP para tirar dinámicamente todos los enlaces de descarga necesarios:

//Mostrando todos los archivos
$getDocuments = mysql_query("SELECT * FROM uploads WHERE ID='".$_SESSION['client_ID']."'");
while($row = mysql_fetch_array($getDocuments, MYSQL_ASSOC)) {
 echo "Descarga: <a href='".$row['url']."'>".$row['url']."</a><br />";
}

Nuestro script final

<html>
<head>
<meta charset="ISO-8859-1" />
<title>Documentos</title>
</head>
<body>
<?php
session_start();
if(!isset($_SESSION['username'])){
 header('Location:login.php');
}
require('db.php');
 
if (!empty($_POST)) {
// Dónde se va a colocar el archivo
 $target_path = "uploads/";
// Añade el nombre del archivo original a la ruta de destino.
 // El resultado es "uploads/filename.extension"
 if (!empty($_FILES)) {
 $target_path = $target_path.basename($_FILES['uploadedfile']['name']);
if((!$_FILES["uploadedfile"]["type"] == "image/gif")
 ||(!$_FILES["uploadedfile"]["type"] == "image/png")
 ||(!$_FILES["uploadedfile"]["type"] == "image/jpeg") // "jpeg" for Firefox
 ||(!$_FILES["uploadedfile"]["type"] == "image/pjpeg") // "jpeg" for IE
 ||(!$_FILES["uploadedfile"]["type"] == "application/msword")
 ||(!$_FILES["uploadedfile"]["type"] == "application/pdf")
 &&(!$_FILES["file"]["size"] < 2097152)){
 echo "El archivo no es del tipo o tamaño correcto. Debería ser un .gif, .png, .jpeg/.jpg, .doc, .docx, .pdf o menor de 2Mb.";
 echo "<br />";
 echo "Si tiene que enviarme un archivo diferente de estas especificaciones, no dude en contactarme por correo electrónico a josecarlosnietoramos@hotmail.com. Estas especificaciones son para la seguridad de la página web.<br />";
 } elseif(move_uploaded_file($_FILES['uploadedfile']['tmp_name'], $target_path)) {
 $result = "INSERT INTO uploads (id, url) VALUES ('".$_SESSION['client_ID']."', '$target_path')";
 $sql = mysql_query($result);
 header('Location:documents.php');
 } else {
 echo "Hubo un error al subir el archivo, por favor, ¡prueba de nuevo!<br />";
 }
 }
}
//Mostrando todos los archivos
$getDocuments = mysql_query("SELECT * FROM uploads WHERE ID='".$_SESSION['client_ID']."'");
while($row = mysql_fetch_array($getDocuments, MYSQL_ASSOC)) {
 echo "Descarga: <a href='".$row['url']."'>".$row['url']."</a><br />";
}
?>
<form enctype="multipart/form-data" action="documents.php" method="POST">
<input type="hidden" name="MAX_FILE_SIZE" value="2097152" />
Escoge el archivo a subir: <input name="uploadedfile" type="file" /><br />
<input type="submit" value="Subir archivo" />
</form>
</body>
</html>

*Este script final es una versión correcta que funciona. Tuve alguna confusión al fijar errores menores del código, así que si algo no coincide entre esta versión y el código anterior deberá corregirse.

Terminando

Ahora tenemos una carga básica y una forma de gestionar los documentos en la parte de cliente. Esto ahora parece un poco innecesario, pero no te preocupes, ya que el área de administración estará disponible muy pronto, y entonces el administrador (tú) y el cliente seréis capaces de compartir sin problemas documentos de ida y vuelta.

En lugar de hacer el área de administración, sin embargo, vamos a estar terminando todo desde la perspectiva del cliente, para poder mejorar la planificación y crear el área de administración una vez lo hayamos hecho. La próxima vez, vamos a crear una estructura muy simple de tablón de anuncios, o “tablero de notas” si se prefiere, donde tú y el cliente podréis intercambiar mensajes a los demás, comunicarse en los dos sentidos sin la molestia del correo electrónico.

Anuncios

Un comentario en “Creando un área de cliente para portfolios con PHP y MySQL – Cuarta parte

Ayúdame a mejorar. Deja un comentario:

Por favor, inicia sesión con uno de estos métodos para publicar tu comentario:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s