Título: Clase FileUpload
Fecha de publicación: 17 de marzo de 2005
Versión: 0.1
Licencia: Creative Commons.
Autor: Andrés Hierro Hernández
Esta clase nace con la intención de realizar la común y a menudo ardua labor de subir archivos a un servidor web remoto. Para muchos webmaster esta es una tarea con la que se encontrarán más de una vez a lo largo de sus carreras, pudiendo convertirse en un quebradero de cabeza, o cuando menos en un pérdida de tiempo. Para intentar evitar esto la clase ha sido implementada para cumplir las siguientes tareas:
Los requisitos de la clase son mínimos:
La clase no necesita instalación, simplemente habría que incluirla en nuestro código mediante un include:
<?php
include('/path/hacia/la/clase/class.FileUpload.php');
?>
O en su defecto, incluir el código completo.
<?php
//----------------------------------------------------------------------------------------------------------CLASE
class FileUpLoad {
//------------------------------------------------------PROPIEDADES
var $directorio;
var $nombre_archivo;
var $repetido;
var $tipos_validos;
var $crea_direcotorios;
var $permisos_direcotorios;
var $max_weight;
var $min_weight;
var $errores;
//------------------------------------------------------METODO
function InicializaErrores() {
$this->errores = '';
}
//------------------------------------------------------METODO
function AgregaError($error) {
$this->errores .= $error;
}
//------------------------------------------------------METODO
//vasado en el script encontrado en php.net en la función con cabecera (andrei at bizland dot ro 24-Oct-2004 06:33)
function CreaDirectorioRecursivo($dir, $permisos) {
if (file_exists($dir)) return false;
preg_match_all('/([^\/]*)\/?/i', $dir, $atmp);
$base = '';
foreach ($atmp[0] as $directorio) {
$base .= $directorio;
if((!file_exists($base)) && (!mkdir($base, $permisos))) return false;
}
return false;
}
//------------------------------------------------------METODO
function ExisteArchivo($nombre_archivo) {
if($this->nombre_archivo) $nombre_archivo = $this->nombre_archivo;
switch($this->repetido) {
//sobreescribir
case 1: {
$this->nombre_archivo = $nombre_archivo;
break;
}
//copiar con nuevo nombre
case 2: {
$array_partes = explode('.', $nombre_archivo);
if((count($array_partes)) > 1) {
$extension = '.' . implode('.', array_slice($array_partes, 1));
$nombre = $array_partes[0];
}
else {
$extension = '';
$nombre = $nombre_archivo;
}
$nombre_temp = $nombre . $extension;
$i = 0;
while(file_exists($this->directorio . $nombre_temp)) {
$nombre_temp = $nombre . '_' . $i . $extension;
$i++;
}
$this->nombre_archivo = $nombre_temp;
break;
}
//abortar si el fichero existe
default : {
if(file_exists($this->directorio . $nombre_archivo)) return false;
return true;
break;
}
}
return true;
}
//------------------------------------------------------METODO
function CrearDirectorios($permisos = 0777) {
$this->crea_direcotorios = true;
$this->permisos_directorios = $permisos;
return true;
}
//------------------------------------------------------METODO
function NombreDeArchivo($nombre) {
$this->nombre_archivo = $nombre;
return true;
}
//------------------------------------------------------METODO
function TiposValidos($array_tipos) {
if(is_array($array_tipos)) $this->tipos_validos = $array_tipos;
return false;
}
//------------------------------------------------------METODO
function PesoMaximo($peso) {
$this->max_weight = $peso * 1024;
return true;
}
//------------------------------------------------------METODO
function PesoMinimo($peso) {
$this->min_weight = $peso * 1024;
return true;
}
//------------------------------------------------------METODO
function SiArchivoExiste($repetido) {
$this->repetido = $repetido;
return true;
}
//------------------------------------------------------METODO
function EstableceDirectorio($dir) {
//si no existe el directorio, deseamos crearlo, y lo creamos satisfactoriamente, o si el directorio existe y es escribible
if(((!file_exists($dir)) && ($this->crea_direcotorios == 1) && ($this->CreaDirectorioRecursivo($dir, 0777))) || ((is_dir($dir)) && (is_writable($dir)))) {
$this->directorio = $dir;
}
return true;
}
//------------------------------------------------------METODO
function Subir($form_file_name, $permisos = 0775, $nombre = '') {
$this->InicializaErrores();
if(!empty($nombre)) $this->NombreDeArchivo($nombre);
if(!isset($_FILES[$form_file_name])) {
$this->AgregaError('No se ha recibido ningún archivo.<br>');
}
if(!$this->ExisteArchivo($_FILES[$form_file_name]['name'])) {
$this->AgregaError('El archivo ya existe y no está habilitada la opción de sobreescribir.<br>');
}
if($_FILES[$form_file_name]['error'] != 0) {
$this->AgregaError('El archivo no se ha recibido correctamente.<br>');
}
if(!$this->directorio) {
$this->AgregaError('No tiene permisos para escribir en el directorio seleccionado o para crearlo.<br>');
}
if(($this->min_weight) && ($_FILES[$form_file_name]['size'] < $this->min_weight)) {
$this->AgregaError('El tamaño del archivo no supera el mínimo permitido.<br>');
}
if(($this->max_weight) && ($_FILES[$form_file_name]['size'] > $this->max_weight)) {
$this->AgregaError('El tamaño del archivo es superior al máximo permitido.<br>');
}
if(($this->tipos_validos) && (!in_array($_FILES[$form_file_name]['type'], $this->tipos_validos))) {
$this->AgregaError('El tipo de archivo enviado no es válido.<br>');
}
if((empty($this->errores)) && (!copy($_FILES[$form_file_name]['tmp_name'], $this->directorio . $this->nombre_archivo))) {
$this->AgregaError('No se ha podido subir el archivo, revise los errores previos.<br>');
}
if(!empty($this->errores)) return $this->errores;
chmod($this->directorio . $this->nombre_archivo, $permisos);
return true;
}
}
?>
aquí la clase
Para que la clase funcione son imprescindibles tres elementos html en los cuales no vamos a profundizar. Si desea saber más sobre este tema encontrará buena y agradable información en ignside.net. Estos tres elementos son:
<form method="POST" enctype="multipart/form-data" action="pagina.php">
Archivo: <input type="file" name="archivo" />
<input type="submit" value="Subir" />
</form>
La forma más simple de utilizar esta clase es la siguiente:
<?php
include('/path/hacia/la/clase/class.FileUpload.php');
//creamos la instancia del objeto
$obj_1 = new FileUpload;
//establecemos el directorio dónde copiaremos la imagen
$obj_1->EstableceDirectorio('/directorio/donde/subir/');
//subimos el archivo especificando el nombre del campo del formulario en el que llega el archivo
$obj_1->Subir('archivo')
?>
Si descomponemos esto en pasos, veremos que necesitamos:
Para poder crear directorios, simplemente tendremos que añadir una línea más de código, pero teniendo en cuenta que siempre debe estar antes de la llamada al método "EstableceDirectorio". Ésto no significa que se vaya a crear un directorio necesariamente, ya que si éste existe, ignorará la orden.
Debemos estar atentos también a que la creación de directorios es recursiva, es decir, si existe el directorio /var/www, pero no /var/www/prueba, y especificamos el directorio a crear /var/www/prueba/subdirectorio/otromas, generará todo el árbol de directorios hasta /otromas.
También podemos especificar rutas relativas al estilo ../directorio
<?php
include('/path/hacia/la/clase/class.FileUpload.php');
//creamos la instancia del objeto
$obj_1 = new FileUpload;
//añadimos la opción para crear directorios
$obj_1->CrearDirectorios();
//establecemos el directorio dónde copiaremos la imagen
$obj_1->EstableceDirectorio('/directorio/donde/subir/');
//subimos el archivo especificando el nombre del campo del formulario en el que llega el archivo
$obj_1->Subir('archivo')
?>
¿ Cómo especificar los permisos del directorio ?
Por defecto los permisos asignados a los directorios creados son 777, lo que es muy útil para probar en el localhost, pero quizás no para un servidor en producción. Para especificar los permisos a nuestro gusto, simplemente tendremos que añadirle un parámetro al método "EstableceDirectorio" especificando éstos de manera octal, es decir: 0755, 0777, etcétera.
Éstos permisos sólo son aplicados a los directorios que creamos, para cambiar los permisos de un directorio existente utilice la función chmod.
<?php
include('/path/hacia/la/clase/class.FileUpload.php');
//creamos la instancia del objeto
$obj_1 = new FileUpload;
//añadimos la opción para crear directorios y especificamos sus permisos
$obj_1->CrearDirectorios(0775);
//establecemos el directorio dónde copiaremos la imagen
$obj_1->EstableceDirectorio('/directorio/donde/subir/');
//subimos el archivo especificando el nombre del campo del formulario en el que llega el archivo
$obj_1->Subir('archivo')
?>
¿ Cómo establecer los permisos de un archivo ?
Para establecer los permisos de un archivo que subimos, volveremos a la forma más sencilla del uso de la clase, y en llamada al método "Subir", añadiremos un parámetro más dónde éstos serán especificados cómo se explicó en el punto anterior ¿ Cómo especificar los permisos del directorio ?.
<?php
include('/path/hacia/la/clase/class.FileUpload.php');
//creamos la instancia del objeto
$obj_1 = new FileUpload;
//establecemos el directorio dónde copiaremos la imagen
$obj_1->EstableceDirectorio('/directorio/donde/subir/');
//subimos el archivo especificando el nombre del campo del formulario en el que llega el archivo y sus permisos
$obj_1->Subir('archivo', 0775)
?>
¿ Cómo especificar el nombre del archivo ?
Por defecto los archivos que subimos al servidor toman el nombre del archivo que se encuentra en nuestro cliente, pero los nombres de archivos son algo muy "personal", y quizás deban ser el mismo que un registro en la bd, el programador desee suprimir los espacios... Por ello, es posible especificar un nombre alternativo al del fichero que subimos.
Para hacerlo es necesario añadir un tercer argumento más al método "Subir". ¿Tercero? se preguntará, sí, no hay más remedio ya que si deseamos especificar el nombre de archivo alternativo, debemos especificar los permisos también.
<?php
include('/path/hacia/la/clase/class.FileUpload.php');
//creamos la instancia del objeto
$obj_1 = new FileUpload;
//establecemos el directorio dónde copiaremos la imagen
$obj_1->EstableceDirectorio('/directorio/donde/subir/');
//subimos el archivo especificando el nombre del campo del formulario en el que llega, sus permisos, y el nuevo nombre
$obj_1->Subir('archivo', 0775, 'nuevo_nombre.extension')
?>
¿ Cómo especificar el tamaño mínimo del archivo a subir ?
Para especificar el tamaño mínimo de los archivos a subir debemos hacer una llamada al método "PesoMinimo" especificando como parámetro el tamaño en kb.
<?php
include('/path/hacia/la/clase/class.FileUpload.php');
//creamos la instancia del objeto
$obj_1 = new FileUpload;
//establecemos el directorio dónde copiaremos la imagen
$obj_1->EstableceDirectorio('/directorio/donde/subir/');
//especificamos el tamaño mínimo del archivo
$obj_1->PesoMinimo(20);
//subimos el archivo especificando el nombre del campo del formulario en el que llega el archivo
$obj_1->Subir();
?>
¿ Cómo especificar el tamaño máximo del archivo a subir ?
Para especificar el tamaño máximo de los archivos a subir debemos hacer una llamada al método "PesoMaximo" especificando como parámetro el tamaño en kb.
<?php
include('/path/hacia/la/clase/class.FileUpload.php');
//creamos la instancia del objeto
$obj_1 = new FileUpload;
//establecemos el directorio dónde copiaremos la imagen
$obj_1->EstableceDirectorio('/directorio/donde/subir/');
//especificamos el tamaño máximo del archivo
$obj_1->PesoMaximo(500);
//subimos el archivo especificando el nombre del campo del formulario en el que llega el archivo
$obj_1->Subir();
?>
¿ Cómo especificar los tipos de archivo permitidos ?
Para especificar los tipos de archivos permitidos permitidos recurrimos a los tipos MIME (Ing. Multipurpose Internet Mail Extensions), Tienes una lista de ellos en el Apéndice A.
La forma de hacerlo es muy sencilla: definimos una matriz con los tipos válidos, y se los pasamos al método "TiposValidos" como argumento. Si omitimos la llamada a este método, la clase aceptará cualquier tipo de archivo.
<?php
include('/path/hacia/la/clase/class.FileUpload.php');
//creamos la instancia del objeto
$obj_1 = new FileUpload;
//establecemos el directorio dónde copiaremos la imagen
$obj_1->EstableceDirectorio('/directorio/donde/subir/');
//declaramos los tipos MIME válidos
$tipos_validos[] = 'image/jpeg';
$tipos_validos[] = 'image/png';
//especificamos los tipos válidos
$obj_1->TiposValidos($tipos_validos);
//subimos el archivo especificando el nombre del campo del formulario en el que llega el archivo
$obj_1->Subir('archivo');
?>
¿ Qué hacer cuando el nombre de un archivo ya existe ?
Por defecto, cuando intentamos subir un archivo y éste ya existe en la ruta especificada, la clase aborta el proceso. Este proceso puede ser variado usando el método "SiArchivoExiste". Este método acepta dos valores como argumento: 1 y 2 (enteros), si usamos cualquier otro valor actuará con la opción por defecto. Según dicho argumento la clase se comportará de las siguientes maneras:
<?php
include('/path/hacia/la/clase/class.FileUpload.php');
//creamos la instancia del objeto
$obj_1 = new FileUpload;
//establecemos el directorio dónde copiaremos la imagen
$obj_1->EstableceDirectorio('/directorio/donde/subir/');
//especificamos la acción a realizar si el nombre de archivo ya existe en la ruta especificada
$obj_1->SiArchivoExiste(2);
//subimos el archivo especificando el nombre del campo del formulario en el que llega el archivo
$obj_1->Subir('archivo');
?>
¿ Cómo saber el nombre del archivo subido si hemos habilitado la opción para renombrar ?
Si hemos utilizado el método "SiArchivoExiste" especificando la opción de renombrar (2), quizás podamos tener un problema, ya que no sabemos realmente si el archivo ha sido renombrado o no, y quizás queramos saber el nombre del archivo. Para averiguarlo, simplemente deberemos acceder a la propiedad: "nombre_archivo" como se muestra en el siguiente código:
<?php
include('/path/hacia/la/clase/class.FileUpload.php');
//creamos la instancia del objeto
$obj_1 = new FileUpload;
//si el archivo existe, lo renombramos
$obj_1->SiArchivoExiste(2);
//establecemos el directorio dónde copiaremos la imagen
$obj_1->EstableceDirectorio('/directorio/donde/subir/');
//subimos el archivo
$obj_1->Subir('archivo');
//obtenemos el nombre del archivo
$nombre_del_archivo = $obj_1->nombre_archivo;
?>
¿ Cómo saber si ha ocurrido algún error, y en tal caso, qué error es ?
El método "Subir", devuelve true si el proceso se ha completado satisfactoriamente. En caso contrario, éste devolverá una cadena con la lista de errores encontrada, asignando esta cadena también a la propiedad "errores". Esto puede evitarnos muchos quebraderos de cabeza y nos aporta un método sencillo para saber por qué ha fallado la subida del archivo. Un ejemplo puede ser el siguiente:
<?php
include('/path/hacia/la/clase/class.FileUpload.php');
//creamos la instancia del objeto
$obj_1 = new FileUpload;
//establecemos el directorio dónde copiaremos la imagen
$obj_1->EstableceDirectorio('/directorio/donde/subir/');
//subimos el archivo, y si falla, imprimimos el errror
if($obj_1->Subir('archivo', 0773) !== true) {
echo $obj_1->errores;
}
else {
echo 'La subida ha sido completada satisfactoriamente.';
}
?>
Supongamos que estamos creando una galería de imágenes, y en este momento estamos afrontando la subida de ficheros. A tal tarea le asignamos unas premisas que debe de cumplir y son las siguientes:
Pues bien, nos ponemos manos a la obra, y simplificando mucho la cosa (aquí sólo queremos mostrar la utilidad de esta clase), obtenemos lo siguiente:
<?php
include('/path/hacia/la/clase/class.FileUpload.php');
//...... aquí habría código previo
//declaramos la clase
$obj_1 = new FileUpLoad;
//añadirmos la opción para la creación de directorios
//especificando los permisos que le asignaremos
$obj_1->CrearDirectorios(0775);
//establecemos el directorio dónde subiremos las fotos
//damos por hecho que la variable $id ya la habíamos obtenido previamente
$obj_1->EstableceDirectorio('/var/www/galerias/'.$id.'/');
//en el caso de que el archivo exita lo reombramos
$obj_1->SiArchivoExiste(2);
//declaramos los tipos válidos, en nuestro caso para una galería
$tipos_validos[] = 'image/jpeg';
$tipos_validos[] = 'image/png';
$tipos_validos[] = 'image/gif';
//asignamos los tipos válidos
$obj_1->TiposValidos($tipos_validos);
//declaramos los tamaños mayor y menor (en kb) para cada archivo a subir
$obj_1->PesoMaximo(500);
$obj_1->PesoMinimo(20);
//si el archivo no es subido correctamente
if($obj_1->Subir('archivo', 0773) !== true) {
//mostramos los errores
$errores .= $obj_1->errores;
}
else {
//si todo hay ido bien, mostramos un mensaje de satisfacción
$errores .= 'El archivo ha sido subido correctamente al directorio '.$obj_1->directorio.' con el nombre '.$obj_1->nombre_archivo.'.<br />';
}
//...... aquí habría código posterior
?>
Aquí os dejo algunos tipos MIME para que no tengáis que buscar mucho. No es una lista completa pero no es un mal punto de partida.
Un buen truco para obtener la lista de tipos mime que nos interesa es imprimir la matriz $_FILES que recibimos al enviar un archivo. Si nos fijamos en el valor de $_FILES['nombre_campo_file']['type'], obtendremos el tipo deseado. Para ello os será útil la función print_r.