05 junio 2007

Subiendo archivos por FTP con INDY

Para subir archivos por FTP utilizaremos el objeto TIdFTP de la paleta de componentes Indy Clients. Para poder utilizar dicho objeto debemos añadirlo en la seccion interface:

uses
Windows, Messages, ......, IdFTP, IdComponent;

La unidad IdComponent la utilizaremos luego para controlar eventos del componente FTP. El objeto lo creamos en tiempo de ejecución:

procedure SubirArchivo( sArchivo: String );
var
FTP: TIdFTP;
begin
FTP := TIdFTP.Create( nil );

Antes de subir un archivo hay que conectar con el servidor dando usuario y password:

FTP.Username := 'usuario';
FTP.Password := 'miclave';
FTP.Host := 'miftp.midominio.com';

try
FTP.Connect;
except
raise Exception.Create( 'No se ha podido conectar con el servidor ' + FTP.Host );
end;

Ahora ya estamos listos para enviar el archivo, pero antes debemos ir al directorio del servidor donde deseamos subir el archivo:

FTP.ChangeDir( '/misarchivos/copiaseguridad/' );

Para subir un archivo tenemos el método Put, el cual toma como primer parámetro la ruta y nombre del archivo a subir, como segundo parámetro el nombre que va a tener el archivo en el servidor (se supone que el mismo) y como tercer parámetro si deseamos añadir a un archivo ya existente o crear el archivo de nuevo:

FTP.Put( sArchivo, ExtractFileName( sArchivo ), False );

En nuestro caso hemos subido el archivo con el mismo nombre y si hubiera otro igual lo sobrescribe. Por último nos desconectamos del servidor y eliminamos el objeto.

FTP.Disconnect;
FTP.Free;
end;

Como suelo comentar en artículos anteriores los componentes Indy no controlan bien la multitarea, por lo tanto si vamos a subir archivos relativamente grandes recomiendo meter el método Put dentro de un hilo de ejecución, ya que si no es así, mientras que el componente TIdFTP no termine de subir el archivo, nuestra aplicación da el aspecto de estar colgada (no se puede ni mover la ventana).

Tampoco estaría mal mostrar al usuario mediante una barra de progreso el estado de la subida del archivo. Para ello el objeto TIdFtp posee el evento OnWork el cual nos informa de los bytes subidos al servidor. El cambio es sencillo.

Primero creamos el evento OnWork:

procedure TFVentana.FTPWork( Sender: TObject; AWorkMode: TWorkMode; const AWorkCount: Integer );
begin
Barra.Position := AWorkCount div 1024;
end;

Se supone que Barra es un componenete TProgressBar donde vamos acumulando el tamaño del archivo enviado. En este caso he dividido los bytes subidos entre 1024 para que me devuelva la información en kilobytes.

Y ahora asociamos el evento al componente después de crearlo:

FTP := TIdFTP.Create( nil );
FTP.OnWork := FTPWork;

¿Cómo averiguamos el tamaño del archivo para medir el progreso? Muy fácil:

procedure SubirArchivo( sArchivo: String );
var
FTP: TIdFTP;
F: File of byte;
begin
AssignFile( F, sArchivo );
Reset( F );
Barra.Max := FileSize( F ) div 1024;
CloseFile( F );
....

Con esto le decimos a la barra de progreso que la longitud máxima de la misma es la longitud del archivo en kilobytes. Una vez comience a subir el archivo la barra de progreso se va incrementando sola según el evento OnWork.

Y por supuesto nunca se os olvide controlar en todo momento el estado de la conexión, tanto para conectar, subir el archivo o desconectarse del servidor capturando las excepciones oportunas e informando al usuario de lo que ocurre (por ejemplo con una barra de estado en la parte inferior de la ventana).

En el próximo artículo mostraré como descargar un archivo por FTP.

Y lo se, a veces los componentes Indy pueden sacar de quicio a cualquiera.

Pruebas realizadas en Delphi 7.

16 comentarios:

Anónimo dijo...

Un buen manual, sencillo y claro, me ha sido de gran utilidad. Gracias

Anónimo dijo...

Enhorabuena por compartir en Internet tus 'creaciones'...
Gracias.
He estado trabajando con las Indy y en concreto con el componente FTP.PUT
Una vez acabada mi pequeña aplicación he descubierto que puede tener un problema importante.
Me explico...
Para poder subir un fichero se requiere poner en la aplicación el nombre de Usuario y la contraseña de acceso a mi servidor.
Acabo de descubrir que con un lector de código hexadecimal, cualquiera puede acceder a esta información si tiene en sus manos esa aplicación.
Lo acabo de comprobar... y es muy fácil de encontrar.
¿Solución?
He creado una función para enmascarar esos identificadores, pero el código EXE se salta la función y aparece directamente el texto descifrado de esos identificadores. Lógico.
Seguiré dandole vueltas al tema...
Si se te ocurre una solución... personalmente lo agradecería.
Un saludo y adelante
Julián

Administrador dijo...

Podías utilizar una sencilla función de encriptación que transforme los caracteres ascii.

Habría que hacer dos funciones: una para encriptar y otra para desencriptar. Hay muchos ejemplos de esto por Internet.

Otra alternativa sería encriptar el ejecutable con programas como UPX, armadillo, etc.

De todas formas, con un sencillo sniffer se pueden ver fácilmente los usuarios y password de cuentas FTP, SMTP y POP.

Lo mejor que puedes hacer también es crear una cuenta FTP de sólo lectura sólo para la tarea que vas a realizar.

Saludos.

Roberto dijo...

Hola antes que nada te envio un agradecimiento por compartir tus conocimientos para algunas personas son muy utiles.
Ahora veras he seguido tus indicaciones paso a paso pero llega a tardar demasiado tiempo en subir un archivo, a comparacion de cualquier gestor de FTP. Que me recomiendas para acelerar la subida (ya intente con un thread y con la propiedad "Priority" en "pHighest").

Administrador dijo...

Podrías utilizar dos o más hilos de ejecución y realizar una conexión con cada uno, pero ojo, una instancia de TIdFTP por cada conexión.

Y mucho cuidado con los hilos, ya que es muy dificil depurarlos cuando están en marcha.

El abrir varias conexiones Indy con hilos a veces no funciona bien, ya que algunos servidores de FTP sólo soportan una conexión por usuario.

En todos estos casos recomiendo que te instales en Windows un buen sniffer para monitorizar tu tráfico. O también programas como NetBalancer que nos dice el ancho de banda que gastamos por programa (de subida y de bajada).

Saludos.

Anónimo dijo...

hola y si deseo hacer la conexion ftp pasiva que debo hacer??

JULY dijo...

Hola! Me ha ayudado mucho tu manual en mis desarrollos. tengo una pregunta, estoy accediendo a un servidor ftp, pero si no hay conexión a internet no quiero que se demore tratando de conectarse, como configuro el número de intentos de comprobación en ftp.connect.
Gracias

JULY dijo...

Hola.
Como puedo controlar el número de intentos de conexión cuando se encuentra el programa en la sentencia ftp.connect

Mil gracias, por tu manual me ha servido muchísimo en mis desarrollos.

Administrador dijo...

Tienes el evento OnConnected, lo que pasa es que sólo funciona si conecta realmente, así que yo lo haría manualmente con un temporizador (TTimer).

Saludos.

CReinoso dijo...

no me sale bien lo del progressbar utilizo el componente backgroud worker para crear los hilos

Administrador dijo...

Como suele pasar con los malditos Indy, es que según la versión de Delphi que estes utilizando, a veces funcionan los eventos OnWork, OnWorkBegin y OnWorkEnd, y otras veces no funcionan.

Por eso mucha gente ha abandonado Delphi. En las versiones nuevas arrenglan unas cosas y rompen otras.

Si yo te contara...

Unknown dijo...

pude alguien subir el codigo fuente en delphi de este ejemplo

Unknown dijo...

me marca un error de undeclared identifier: 'FTPWork'.
pude alguien subir el codigo fuente en delphi de este ejemplo

Unknown dijo...

buenas , antes que nada agradesco que compartas tu conocimiento y espero y me puedas orientar quiero enviar un pdf pequeño cada que un cliente lo solicite y no veo la necesidad de implementar un ftp ya que solo sera un archivo como lo podría hacer ya he buscado mucho en la red pero no hacen algo tan sencillo como eso si no lo envia por correo como adjunto y yo no deseo eso trabajo con indy tcp server y tcpclient

Unknown dijo...

Buenas,
estoy intentando acceder a un FTP, pero me retorna este error al hacer el PUT. "Policy requires SSL.".

Gracias. !!

Pablo Olivares dijo...

Buenas, te puedo consultar algo sobre el Put, yo quiero subir un archivo, me los usbe sin problema pero no quiero que me lo vuelva a subir si ya existe en el ftp, que comando tengo para preguntar si ese archivo ya existe en el ftp y evitar hacer el put de ese archivo ?, no se si se entiende lo que necesitaria ?

Publicidad