[Ir al menú de PHP]
Transfiriendo ficheros

Dos tipos de transferencias

Para tratar de evitarte el confusionismo mental que he tenido que padecer (ya sabes aquello de ... donde Dios no da, Salamanca no presta) empezaré por decirte que mis ideas estuvieron muy confusas hasta que me percaté de que son posibles dos tipos de transferencia: No menciono siquiera una tercera opción -la descarga de ficheros desde un sitio web- por aquello de que su funcionalidad es tan sencilla como escribir un link al fichero en el código de una página web.

La primera de las opciones requiere que el PHP que estemos ejecutando tenga activada la opción file_uploads, mientras que para segunda es necesario que tengamos un servidor FTP y que el PHP tenga configurada la opción FTP support como enabled.

Subir ficheros a un servidor web

Lo primero de todo es comprobar si está activada esta opción. La forma de comprobarlo es recurrir al famoso fichero info.php y a través de el comprobar la configuración de las opción: file_uploads tiene como valor 1.

Si esa opción está activada, conviene comprobar también un par de valores más: upload_max_filesize que nos indicará el tamaño máximo permitido para los ficheros (la configuración por defecto de PHP lo establece en 2 Mb) y también el upload_tmp_dir que nos dirá el nombre del directorio temporal al que llegarán los ficheros (la opción PHP por defecto es no value).

Para efecutar estas transferencias se requieren: un formulario y script PHP.

El formulario

Requiere que dentro de la etiqueta <FORM> contenga -además del METOHD y de la ACTION- exactamente esto:

ENCTYPE="multipart/form-data"
y que dentro del formulario incluyamos un <INPUT TYPE=FILE>

El script

Al enviar el formulario anterior, aparte de transferir el fichero elegido en el input type=file transfiere al script PHP la variable definida en el valor name de la etiqueta anterior.

Esa variable -a la que llamaré $archivo para ejemplificar- tiene una importante peculiaridad ya que -aparte de recoger el fichero transferido- lleva implicitos tres valores muy importantes:

Pues bien, bastaría con que este script contuviera una instrucción
copy($archivo, $nombre)
para que el fichero transferido a través del formulario fuera guardado en el directorio actual del servidor con el nombre asignado en la cadena $nombre.

También es posible guardar el cualquier otro directorio del servidor. Para ello basta con incluir en la cadena $nombre el path del directorio en el que pretendemos guardar.

Cuando register_globals está en off

El script que te comento en el párrafo anterior no funcionaría en el caso de que php.ini tuviera configurada en off la opción register_globals

Ahora los parámetros name, size, type así como el nombre del fichero temporal resultante de la transferencia serían recogidos en un array asociativo bidimensional $HTTP_POST_FILES cuyo primer índice sería el valor pasado desde el formulario como name (en INPUT type="file") y cuyos segundos índices serían respectivamente name, size, type y el nuevo e importante tmp_name bajo el que se recogería el nombre asignando al archivo transferido en el fichero temporal.

La función copy debe utilizar -fíjate en el ejemplo- ese nombre temporal.

Tal como te comenté en el apartado relativo a formularios, al tener desactivado el famoso register_globals todas las demás variables transferidas desde el formulario han de ser recogidas mediante $HTTP_POST_VARS ó -si la versión de PHP es superior a 4.1.0- mediante la variable superglobal $_POST.

La opción alternativa -mejor dicho complementaria... ya que funcionan ambas- a $HTTP_POST_FILES serí el uso de la superglobal $_FILES que recogería el mismo array bidimensional que te acabo de comentar. Como es lógico, esta opción -igual que las demás superglobales- fué incorporada a partir de la versión 4.1.0 de PHP.

Transferencias FTP

PHP tiene funciones -activas cuando en info.php se ve FTP support como enabled- que permiten efectuar transferencia de ficheros en ambas direcciones, además de las de crear y modificar directorios en el servidor y conocer información sobre los fichero que contienen.

Los dos primeros pasos -para trabajar con estas funciones- serán siempre abrir la conexión y pasar el login y lo último será cerrar la conexión

Abrir y cerrar conexión

$x=ftp_connect (host,pt)

Esta función en la que host es una cadena con el nombre del servidor FTP (no te olvides de ponerlo entre comillas) y pt es el número del puerto a través del cual se efectúa la conexión FTP.
Si se omite pt se asigna por defecto el valor 21 que es el habitual de todos los servidores FTP.
La variable $x recogerá el identificador de la conexión que será utilizado por las demás funciones.

ftp_login($x,user,pass)

Después de efectuar la conexión es preciso comenzar la sesión utilizando la función ftp_login con los siguientes parámetros:
Esta función devuelve un valor booleano que será 1 en el caso en que se inicie la sesión correctamente o false si no lo hace.

ftp_quit($x)

Esta función cierra la conexión abierta con el identificador indicado en la variable $x.


Directorio raíz

El directorio raíz será identificado mediante el símbolo / y será distinto dependiendo del nombre de usuario que hace el login.

En todos los casos tomará el que se hubiera configurado como tal para ese usuario concreto.

Información sobre contenidos

ftp_nlist($x, nomdir)

Devuelve una array escalar que contiene los nombres de los ficheros y los directorios contenidos en el directorio indicado en nomdir

Si se pretende extraer esa información del directorio actual, el parámetro nomdir puede especificarse como una cadena vacia ("").

Si lo que se pretende es extraer la información de un subdirectorio del directorio actual, basta con poner el nombre de ese subdirectorio como valor del parámetro nomdir.

En otro caso, nomdir contendrá el path completo del directorio a partir del raiz.

ftp_rawlist($x, nomdir)

Igual que la función anterior, ftp_rawlist también devuelve un array escalar pero en este caso conteniendo una información más amplia.

Por cada fichero presenta información relativa a los permisos, tipo, tamaño, fecha de la última modificación, además del nombre del fichero.

Cambiar de directorio

ftp_chdir($x, nuevodir)

Cambia al directorio especificado por la cadena nuevodir.

ftp_pwd($x)

Devuelve el nombre del directorio actual

ftp_cdup($x)

Cambia al directorio raiz del servidor

Crear y borrar directorios en el servidor FTP

ftp_mkdir($x, nomdir)

Esta función crea un subdirectorio con el nombre indicado en la cadena nomdir dentro del directorio actual.

Aunque no vengo haciendo alusión al parámetro $x -tanto en esta función como en las anteriores y en las sucesivas- hace alusión a resultado de la funcion ftp_connect.

ftp_rmdir($x, nomdir)

Esta función borra el subdirectorio con el nombre indicado en la cadena nomdir y contenido en el directorio actual.

Para que un directorio pueda ser borrado se requiere que sea un directorio vacio.

Transferir ficheros

ftp_get($x,nloc,nrem,modo)

Esta función transfiere un fichero desde un servidor FTP hasta un directorio del servidor donde está instalado el fichero PHP que se está ejecutando.

La cadena nloc contiene el nombre con el que el fichero será copiado en el directorio actual del servidor web (el mismo donde está el fichero php que contiene la función) y la cadena nrem contiene el nombre (incluyendo el path) del fichero (alojado en el servidor FTP) que vamos a trasferir. El parámetro modo puede contener uno de estos valores: FTP_ASCII ó FTP_BINARY

ftp_put($x,nrem,nloc,modo)

Esta función transfiere un fichero desde un directorio del servidor donde está instalado el fichero PHP que se está ejecutando hasta un servidor FTP.

Se de forma idéntica a la función anterior. La cadena nrem sigue siendo el nombre y el path del servidor FTP (donde ahora copiaremos el fichero) y nloc contiene el nombre del fichero en el servidor web (desde donde haremos la transferencia).

Modificar ficheros en el servidor FTP

ftp_rename($x,nant,nnuevo)

Cambia el nombre de un fichero existente (indicado en la cadena nant por el nuevo nombre indicado en la cadena nnuevo.

ftp_delete($x,fichero)

Borra -en el servidor FTP- el fichero indicado en la cadena fichero.

Información sobre ficheros del en el servidor FTP

ftp_size($x,nomfile)

Devuelve el tamaño (en bytes) de fichero indicado en la cadena nomfile.

ftp_mdtm($x,nomfile)

Esta función devuelve la fecha de la ultima modificación del fichero indicado en la cadena nomfile. Este valor viene expresado en Tiempo Unix.


 
 


Subir ficheros a un servidor

Aquí tienes un ejemplo de como subir ficheros a un servidor. El formulario podría ser algo como esto...

<HTML>
<BODY>

<FORM ENCTYPE="multipart/form-data" ACTION="script.php" METHOD="post"> 

#con este input "oculto" establecemos el limite máximo
# del tamaño del fichero a transferir. En este ejemplo 50.000 bytes
<INPUT type="hidden" name="lim_tamano" value="50000"> 

<p><b>Archivo a transferir<b><br> 

<INPUT type="file" name="archivo"></p> 

<p><INPUT type="submit" name="enviar" value="Aceptar"></p>

</FORM>

</BODY>

</HTML>



Podrás observar que he incluido un input oculto (hidden) en el formulario conteniendo un variable con un valor (en bytes) para el tamaño máximo que puede ser utilizada en el script para impedir la copia de ficheros que excedan ese tamaño preasignado.


Ver form63a.html

Por razones obvias el formulario de ejemplo tiene desactivado el script cuyo código fuente sería algo como esto...

<?

if ($archivo != "none" AND $archivo_size != 0 
                                   AND $archivo_size<=$lim_tamano){
		
    if (copy ($archivo, $archivo_name)) {
	  echo "<h2>Se ha transferido el archivo $archivo_name</h2>";
	  echo "<br>Su tamaño es: $archivo_size bytes<br>";
	  echo "<br>El fichero es tipo: $archivo_type <br>";
               }
}else{
    echo "<h2>No ha podido transferirse el fichero</h2>";
    echo "<h3>su tamaño no puede exceder de $lim_tamano bytes</h2>";
}
 
?>


¡¡Una advertencia...!!

El procedimiento que comento lo he comprobado en el servidor que aloja estas página y que tiene configurada la opción PHP upload_tmp_dir como no value y también lo he comprobado en modo local tanto con la opcion no value como estableciendo en el fichero php.ini un directorio para esa opción, pero...
según he leido en la gües, en servidores UNIX que tienen asignado un directorio para opción upload_tmp_dir se requiere hacer un primer copy incluyendo el path del upload_tmp_dir y luego un segundo copy para transferir el fichero de esa ubicación temporal a la definitiva.
No he podido comprobarlo -en estas cosas soy como Santo Tomás... :-)- pero... advertid@ quedas, por si las moscas...


Para el caso de que register_globals=off


<?
# recogemeos en variables con el nombre de las usadas en el script anterior
# los valores transferidos por $HTTP_POST

$archivo_name= $HTTP_POST_FILES['archivo']['name'];
$archivo_size= $HTTP_POST_FILES['archivo']['size'];
$archivo_type=  $HTTP_POST_FILES['archivo']['type'];
$archivo= $HTTP_POST_FILES['archivo']['tmp_name'];

$lim_tamano= $HTTP_POST_VARS['lim_tamano'];

# opción alternativa a la anterior
# para versión PHP 4.1.0 o posterior

# $archivo_name= $_FILES['archivo']['name'];
# $archivo_size= $_FILES['archivo']['size'];
# $archivo_type=  $_FILES['archivo']['type'];
# $archivo= $_FILES['archivo']['tmp_name'];

# $MAX_FILE_SIZE= $_POST['lim_tamano'];
# a partir de aquí el script es idéntico al anterio


if ($archivo != "none" AND $archivo_size != 0 
                                   AND $archivo_size<=$lim_tamano){
		
    if (copy ($archivo, $archivo_name)) {
	  echo "<h2>Se ha transferido el archivo $archivo_name</h2>";
	  echo "<br>Su tamaño es: $archivo_size bytes<br>";
	  echo "<br>El fichero es tipo: $archivo_type <br>";
               }
}else{
    echo "<h2>No ha podido transferirse el fichero</h2>";
    echo "<h3>su tamaño no puede exceder de $lim_tamano bytes</h2>";
}
 
?>


Una sintaxis alternativa

PHP posee una función alternativa a la copy que he utilizado en los ejemplos anteriores. Se trata de move_uploaded_file.

Para usar esta nueva función basta con que cambies -en los scripts anteriores- la palabra copy por move_uploaded_file manteniendo los demás parámetros de aquella función.

La ventaja opcional de esta nueva función es que comprueba que el fichero ha sido transferido mediante HTTP POST y además -cuando la opción safe mode=on- comprueba también que el fichero haya sido transferido por el mismo usuario que hace la petición de ejecución del script. En definitva, añade seguridad a la transferencia.... ;-)


Probando las funciones de FTP

Antes de empezar con los experimentos conviene tener claros algunos conceptos que -al menos en mi caso- las circunstancias coyunturales (¿verdad que me ha quedado fino...? :-)) pueden hacernos confundir.

Esta opción parece estar concebida básicamente para el caso en el que dispongamos de dos servidores distintos, alojados en dos sitios distintos y con características distintas.

Uno de ellos el servidor FTP –cuyo proceso de instalación y configuración tienes descritos en este enlace– funcionaría como almacén de ficheros y el otro el servidor web funcionaría como almacén de páginas web y también como centro de ejecución de los scripts PHP.

Con estas funciones de PHP se trataría de gestionar el servidor FTP desde el servidor web.

Sin embargo, ocurre con mucha frecuencia que ambos pueden estar en el mismo sitio e incluso que compartan el mismo directorio raíz.

Conexión, login de usuario y desconexión

<?
# conexión con el servidor FTP
if($x=@ftp_connect ("localhost",21)){
    echo "Conexión FTP activada";
}else{
    echo "No se activo lo conexión FTP";
}
# registro de usuario
if(@ftp_login($x,"admin","pepa")){
    echo "El login y la password han sido aceptados";
}else{
    echo "Error en login o password";
}
#desconexión
ftp_quit($x);
?>


Lista de contenidos del directorio root

<?
if($x=@ftp_connect ("localhost",21)){
    echo "Conexión FTP activada<br>";
}else{
    echo "No se activo lo conexión FTP";
}
if(@ftp_login($x,"profes","profes")){
    echo "El login y la password han sido aceptados<BR><BR>";
}else{
    echo "Error en login o password";
}
$lista=ftp_nlist($x,"/");
foreach($lista as $c=>$v){
    print "Indice: ".$c." Valor: ".$v."<br>";
}
print "<H1>Lista completa</H1>";
$listacompleta=ftp_rawlist($x,"/");
foreach($listacompleta as $c=>$v){
     print "Indice: ".$c." Valor: <H1>".$v."</H1>";
}
ftp_quit($x);
?>

El resultado de la ejecución del script anterior podría producir una salida similar a esta:



Tal como puedes ver en la imagen, la cadena devuelta por la función ftp_rawlist tiene dos resultados distintos. La primera de las cadenas comienza por lo cual indica que se trata de un archivo y documento. En el segundo de los casos ese primer carácter es d e indica que se trata de un directorio.

Los nueve caracteres siguientes especifican los permisos de acceso a los ficheros y/o directorios. Se subdividen en tres bloques de igual tamaño que corresponden a los tres niveles de usuarios habituales en sistemas Unix/Linux (propietario, grupo y resto de usuarios). Para nuestros propósitos bastará con que consideremos los privilegios del primer bloque, es decir los del propietario.

El primero carácter de cada bloque sólo puede ser r ó . Si se trata de un fichero y está marcado con r indica que se permite el acceso a él en modo lectura y si se trata de un directorio indica que está permitida la visualización de su contenido.

El segundo de los caracteres (puede ser w ó ) indica, si se trata de un fichero, que está permitida la modificación del fichero. Cuando se trata de un directorio significa que se pueden añadir o suprimir ficheros.

El tercero de los caracteres indicaría (x ó ) que el fichero -si se trata de un ejecutable- tiene permisos para ser ejecutado. Cuando se trata de un directorio, indica que pueden conocerse los atributos de los ficheros que contiene y que está permitido el acceso a él y a sus subdirectorios.

El signo significa la negación del atributo en todas las opciones.

El siguiente caracter, el número 1, está asociado con sistemas Linux/Unix e indicaría el número de vínculos duros contra el archivo, que es otra cosa que una forma de asignar nombres distintos a un mismo fichero.

Los dos grupos siguientes -parece que no demasiado relevantantes para nuestros propósitos- ser los nombres del usuario y grupo al que pertenece.

A continuación aparece el tamaño del archivo (cero si se trata de un dierectorio), la fecha y hora de su creación y el nombre del archivo o directorio.

Miscelánea de funciones FTP


<?
# Conexión con el el servidor ftp
if(!$x=@ftp_connect ("localhost",21)){
    echo "No se activo lo conexión FTP";
	exit();
}
# Identificación de usuario
if(!@ftp_login($x,"admin","admin")){
    echo "Error en login o password";
	exit();
}
/* comprobamos el nombre del directorio actual del servidor FTP 
que será el root correspondiente al usuario registrado */
echo "El directorio actual es: ",ftp_pwd($x),"<br>";

/* intentamos cambiar a un subdirectorio indicando la ruta absoluta
partiendo del directorio root del usuario actual.
En caso de error (ruta incorrecta o falta de permisos de accesos
nos daría un mensaje de error. Si el cambio tiene éxito nos indicaría
el nombre del nuevo directorio */
if(!@ftp_chdir($x,"/Apache/htdocs")){
    print "No tienes permisos de acceso a este directorio<br>";
    print "o la ruta es incorrecta.¡Comprueba los datos!<br>";
  }else{
    echo "El directorio actual es: ",ftp_pwd($x),"<br>";
}
# comprobamos el nombre del sistema operativo del servidor de FTP
echo "El S.O: del servidor FTP es: ",ftp_systype ($x),"<br>";

/* obtenemos una matriz conteniendo la lista de ficheros y directorios 
 del subdirectorio "Materiales" del del directorio actual*/
$lista=ftp_nlist($x,"Materiales");
# escribimos la lista de ficheros contenidos en ese directorio
echo "Lista de ficheros contenidos en el subdirectorio Materiales<br>";
     foreach ($lista as $valor){
           echo $valor,"<br>";
     }
# obtenemos una lista completa de los contenidos de ese subdirectorio
$lista=ftp_rawlist($x,"Materiales");
# ordenamos el array que contiene la lista anterior
sort($lista);
echo "Contenidos del subdirectorio Materiales<br>";
/* extrae los elementos del array eliminando los espacios repetidos
mediante la funcion preg_replace en la que \s+ indica uno o más espacios  
  que serán sustituidos por uno solo (' ') */
foreach($lista as $v){
          $v=preg_replace('/\s+/', ' ', $v);
       # imprimimos la cadena completa
          print "<br><BR><BR>".$v."<br>";
       # convertimos la cadena en un array 
       # utilizando los espacios como separadores
          $extrae=explode(" ",$v);
       # leemos los elementos del array y comentamos sus valores
   foreach($extrae as $indice=>$cont){
     switch($indice){   
       case 0:
         print "El elemento de indice".$indice." es: ".$cont."<br>";
         if (substr($cont,0,1)=="d"){
            print "Es un directorio<br>";
         }elseif(substr($cont,0,1)=="-"){
            print "Es un fichero<br>";
         }
		 if (substr($cont,1,1)=="r"){
            print "Tiene permisos de LECTURA<br>";
         }elseif(substr($cont,1,1)=="-"){
            print "No tiene permisos de LECTURA<br>";
         }
        if (substr($cont,2,1)=="w"){
            print "Tiene permisos de ESCRITURA<br>";
         }elseif(substr($cont,2,1)=="-"){
            print "No tiene permisos de ESCRITURA<br>";
         }
       break;
       case 4:
           print "El tamaño de este fichero es: ".$cont." bytes<br>";
       break;
       case 8:
           print "El nombre del fichero o directorio es: ".$cont."<br>";
       break;
		
     }
   }
}
/* creamos un subdirectorio (del directorio actual)
con nombre experimento anteponiendo @# para evitar mensajes de error 
en caso de que ya existiera */
@ftp_mkdir($x,"experimento");
/* copiamos el fichero cabina.jpg desde el directorio que se indica
   en el tercer parámetro al directorio del servidor FTP que se indica
   en el segundo parámetro. Le ponemos por nombre calixto.jpg */
ftp_put($x, "/Apache/htdocs/experimento/calixto.jpg",
                     "../Materiales/cabina.jpg",FTP_BINARY);
# obtenemos el tamaño del fichero transferido
echo "El tamaño de fichero tranferidos es: ",
ftp_size($x,"/Apache/htdocs/experimento/calixto.jpg")," bytes<br>";
/* escribimos la fecha de la última modificación del fichero transferido
 que coincidirá con la fecha y hora en la que se realizó la transferencia.
 Convertimos a formato de fecha convencional el tiempo UNIX que devuelve
 la función ftp_mdtm */
print "La fecha de modificacion del fichero es:";
print date("d-m-Y H:i:s", 
     ftp_mdtm($x,"/Apache/htdocs/experimento/calixto.jpg"));
# cambiamos el nombre del fichero calixto.jpg por filiberto.jpg
# en el servidor FTP
@ftp_rename($x,"/Apache/htdocs/experimento/calixto.jpg",
              "/Apache/htdocs/experimento/filiberto.jpg");
/* creamos un enlace de descarga directa del fichero haciendo una llamada
   mediante el protocolo ftp:// utilizando la sintaxis:
   ftp://usuario:contraseña@nombre del servidor
   seguidos de la ruta (en el servidor FTP) y el nombre del fichero */
print "<BR><A href='ftp://admin:admin@localhost";
print "/Apache/htdocs/experimento/filiberto.jpg'> Descargar</a>";
/* transferimos al directorio del servidor Apache
   indicado en el segundo parámetro (con el nombre que se indica
   en el mismo) un fichero procedente del servidor FTP
   cuyo nombre y ruta se indican en el tercer parámetro*/
ftp_get($x,"../Materiales/tiburcio.jpg",
           "/Apache/htdocs/experimento/filiberto.jpg",FTP_ASCII);
/* comprimimos un fichero alojado en el servidor Apache 
      para transferirlo comprimido al servidor FTP */

     #empezamos leyendo el fichero y guardándolo en una cadena
     $f1=fopen("../Materiales/cabina.jpg","r");	
          while (!feof($f1)) {
                  $cadena .= fgets($f1,1024);
            }
     fclose($f1);
    # comprimimos la cadena obtenida del fichero anterior
          $c1=gzencode($cadena,3,FORCE_GZIP);
    # guardamos la cadena comprimida en un fichero
          $f=fopen("cabina.jpg.gz","w");
          fwrite($f,$c1);
          fclose($f);
/* al servidor el fichero comprimido. No es necesario indicar
  la ruta actual ya que ha sido creado en el mismo directorio
  en el que se está ejecutando el script */
ftp_put($x, "/Apache/htdocs/experimento/cabina.jpg.gz",
              "cabina.jpg.gz",FTP_BINARY);
	#eliminamos el fichero comprimido del servidor Apache
 unlink("cabina.jpg.gz");
 # cerramos la conexión con el servidor ftp
ftp_quit($x);
# establecemos un enlace de descarga para el fichero comprimido
print "<BR><A href='ftp://admin:admin@localhost";
print "/Apache/htdocs/experimento/cabina.jpg.gz'>Descarga comprimido</a>";
?>


Transferencia desde el ordenador local al servidor FTP

Los procesos de transferencia que hemos visto en el ejemplo anterior se realizan siempre desde el servidor HTTP hacia el servidor FTP ó viceversa.

El proceso de descarga desde el servidor FTP lo hemos incluido en el ejemplo, pero tenemos aún un problema sin resolver. ¿Cómo transferir un fichero desde mi ordenador al servidor FTP sin utilizar un cliente FTP?.

La solución estriba en hacer el proceso en tres pasos. Primero trasnferimos (mediante un formulario) al servidor HTTP. Luego hacemos la transferencia entre ambos servidores y, por último, borramos (del servidor HTTP) el fichero transferido.

Aquí tienes el código fuente de un ejemplo (más o menos completo) de cómo realizar transferencias (en ambos sentidos) a un servidor FTP.


Ver código fuente

Sugerir a un/a amig@ Envíame tus comentarios
Anterior
Indice
Siguiente