Hola amigos, en este articulo veremos como podemos construir Clientes Ftp en delphi, a través del componente NMFTP incluido en la ficha Fasnet. Por cierto Delphi 7 no contiene esta ficha pero puedes adquirir el componente en la pagina de sus creadores la cual es http://www.netmastersllc.com, claro esto con una modica suma. Bueno pero para los que tienen Delphi 7 no se preocupen, ya que trae la ficha Indy Clients y ahí viene un componente que se llama IdFtp con el cual pueden desarrollar su cliente, claro no es exactamente igual pero hacen lo mismo.
Bueno, como quizás ya sepan, FTP significa Protocolo de Transferencia de Archivos (File Transfer Protocol), este servicio comúnmente utiliza el puerto 21 para conectar los Sockets y nos permite precisamente eso, realizar transferencias de archivos entre un cliente y un servidor. Si no sabes lo que es un socket te recomiendo que entres al articulo que escribí sobre ellos (Articulo sobre Sockets).
En nuestro caso desarrollaremos un pequeño pero muy potente sistema de Cliente FTP, con el cual nos podremos conectar a un Servidor de FTP y realizar las operaciones mas comunes, según el tipo de cuenta y permisos que tengamos.
NOTA: Al final pongo el código fuente, pero aprenderías mas si tratas de hacerlo primero.
Es necesario entonces que describa las propiedades y eventos mas importantes del componente NMFTP.
Propiedad / Evento | Descripción |
CurrentDir |
Contiene el nombre del directorio actualmente ocupado en el sistema
remoto, si se cambia de directorio también cambia esta propiedad.
|
FTPDirectoryList |
Es utilizada solamente cuando la propiedad de ParseList se pone en True.
FTPDirectoryList contiene cada uno de los elementos del listado del
directorio separado en características como el nombre del archivo, tamaño,
atributos, etc.
|
OnListItem |
Este evento ocurre cuando se está realizando un listado
del directorio remoto.
|
ParseList |
Es de tipo Boolean y según este activa o no analiza
los listados entrantes al directorio en la Propiedad FTPDirectoryList.
|
Password |
Especifica la contraseña usada para abrir una sesión
en el servidor remoto de ftp.
|
UserID | Es el nombre de usuario con el cual se conectara al servidor remoto de ftp. |
Vendor |
Especifica el tipo de servidor de ftp
con el cual está siendo conectado. Esto permite que el componente analice
los listados del directorio enviados del servidor de la manera apropiada.
El valor por defecto detecta el tipo de servidor, pero de cualquier forma
aquí tienes la lista de constantes que puedes utilizar:
NMOS_WINDOWS, NMOS_VM, NMOS_BULL, NMOS_MAC NMOS_TOPS20, NMOS_VMS, NMOS_OS2, NMOS_MVS_IBM, NMOS_MVS_INTERLINK, NMOS_OTHER, NMOS_AUTO, NMOS_NT NMOS_TANDEM, NMOS_AS400 NMOS_OS9, NMOS_NETWARE |
BeenCanceled |
Es de tipo Boolean y se activa a True cuando
la operación ha sido cancelada.
|
BeenTimedOut |
Es de tipo Boolean y esta en True cuando la operación actual ha
medido el tiempo de espero, o FALSA si la operación actual no ha medido el
tiempo de espero
|
BytesRecvd |
Contiene el número de Bytes
recibidos de la transferencia de datos actual.
|
BytesSent |
Contiene el número de Bytes enviados
durante la transacción actual de los datos.
|
BytesTotal |
BytesTotal contiene el número total de Bytes para
enviar o para recibir en la transacción actual de los datos.
|
Connected |
Esta en True si el cliente está conectado
actualmente con el servidor remoto, y False si el cliente no está
conectado.
|
Host |
Contiene el nombre o la dirección IP del
servidor remoto a conectar.
|
LastErrorNo |
Contiene el ultimo error reportado del Socket. |
LocalIP |
Contiene la dirección IP de la
computadora local.
|
port | Especifica el numero de puerto con el cual se conectara al servidor de ftp, comúnmente suele ser el numero 21. |
TimeOut |
Especifica la cantidad de horas (en
milisegundos) para esperar una respuesta del host antes de que se levante
una excepción y se aborte la operación actual. Si el tiempo es 0, una
excepción nunca se levanta, y las operaciones nunca miden el tiempo de
espera.
|
TransactionReply |
Contiene el resultado del ultimo comando enviado al servidor. |
OnAuthenticationFailed |
Se llama al evento OnAuthenticationFailed
cuando la contraseña es
inválida. Esto podría ser debido un nombre de cuenta inválido (UserID), a
o una contraseña inválida.
|
OnAuthenticationNeeded |
Se llama al evento OnAuthenticationNeeded
cuando la propiedad UserID o la propiedad Password está en
blanco. Si el parámetro manejado se deja en False, una excepción será levantada, y la conexión será abortada,
pero si el
parámetro manejado se fija a True, y se proporcionan un UserID y/o una
contraseña, la autentificación será reintentada otra vez. Si falla una
segunda vez, una excepción será levantada y se aborta la conexión.
|
OnTransactionStart |
Se llama al evento OnTransactionStart
cada vez que los datos se envían del servidor remoto a la computadora
local usando el Socket de Datos.
|
OnTransactionStop |
Se llama al evento OnTransactionStop
cuando una transferencia de datos del servidor remoto de ftp a la computadora local ha
terminado.
|
OnUnSupportedFunction |
Se llama al evento OnUnSupportedFunction cuando un comando del
ftp no puede ejecutarse en el servidor remoto de ftp. El parámetro de Trans_Type
especifica qué comando no pudo ejecutarse. Los valores posibles para
este parámetro se listan a continuacion. cmdChangeDir, cmdMakeDir, cmdDelete,
cmdRemoveDir, cmdList, cmdRename, cmdUpRestore, cmdDownRestore, cmdDownload,
cmdUpload, cmdAppend, cmdReInit, cmdAllocate, cmdNList, cmdDoCommand,
cmdCurrentDir. La forma de utilizarlo sera untilizando dentro de este
evento un case Trans_Type of
y a cntinacion las constantes y el codigo que quieres que se ejecute. |
OnAccept |
Se llama al evento OnAccept cuando hay
una conexión entrante al Socket, y el método Listen se ha invocado con el
parámetro Sinc como FALSO.
|
OnConnect |
Se llama al evento OnConnect cuando una conexión se establece
con el servidor remoto. Esto corresponde al mensaje de FD_CONNECT del
Winsock.
|
OnConnectionFailed |
Se llama al evento OnConnectionFailed cuando
una conexión no puede ser establecida con el servidor remoto.
|
OnConnectionRequired | Se llama al evento de OnConnectionRequired siempre que se llamen métodos que requieran estar conectado con el servidor remoto |
OnDisconnect |
El evento OnDisconnect es llamado cuando el cliente
se desconecta del
servidor. Esto corresponde al mensaje de FD_CLOSE del Winsock.
|
OnError |
Se llama al evento OnError cuando ocurre un error del Winsock.
El parámetro de ErrNo especifica la representación numérica del error que
ocurrió, y el parámetro de ErrMsg es el mensaje de error asociado al
número del error.
|
OnHostResolved |
Se llama al evento OnHostResolved cuando
el Host ha respondido a la conexión.
|
OnInvalidHost |
Se llama al evento OnInvalidHost cuando el
Host
especificado es inválido. Si el
parámetro manejado se fija a True, después de la conexión. si no una
excepción se levanta.
|
OnPacketRecvd |
Se llama al evento OnPacketRecvd cuando los datos se reciben
del servidor remoto. Utilizado conjuntamente con las propiedades BytesRecvd y de BytesTotal el progreso de una transacción de los datos
puede ser supervisado.
|
OnPacketSent |
Se llama al evento OnPacketSent cuando los datos se envían al
servidor remoto. Utilizado conjuntamente con la propiedad BytesSent
y la propiedad BytesTotal, el progreso de una
transferencia de datos puede ser supervisado.
|
OnRead |
Se llama al evento OnRead cuando hay datos entrantes que son
enviados del servidor remoto. Esto corresponde al mensaje de FD_READ del
Winsock.
|
OnStatus | Es llamado cuando surge un cambio en el estado del componente. |
Vaya... finalmente termine, como pueden ver es una lista grande de propiedades y eventos, mismos que nos permiten controlar todo lo que sucede con nuestras transacciones en el servidor.
Ahora desarrollaremos nuestro Cliente FTP, Así es que arranca Delphi y crea una nueva aplicación.
Al Caption del Form ponle Cliente FTP . . ., Coloca un Componente CoolBar esta en la ficha Win32, ponlo algo ancho y en su propiedad Align déjala en alTop, dentro del CoolBar coloca dos componentes Panels y sus propiedades align ponlas también en alTop.
Dentro del primer panel pon cuatro SpeedButton´s y su propiedad Flat ponla en True, en orden ponles en su caption lo siguiente: Conectar, Desconectar, Cargar, Descargar. Puedes ponerles alguna imagen alusiva al texto.
En el Segund Panel pon Cuatro componentes Label´s y en orden ponles en su Caption lo siguiente: Host, Usuario, Clave, Puerto. Deja suficiente espacio entre ellas.
Ahora coloca cuatro componentes Edit, quedando el Edit1 para el Host, el Edit2 para el usuario y así sucesivamente. Borrales el contenido en la propiedad Text. Al Edit3 correspondiente al Label Clave en propiedad PasswordChar ponle un asterisco( * ), al Edit4 correspondiente al puerto ponle en su propiedad Text 21.
Coloca un componente Panel, su propiedad Align ponla en alTop, alárgalo de tal forma que ocupe casi todo el formulario, ya que en el pondremos mas componentes. Borra el contenido de Caption.
Coloca dentro del Panel un componente Memo, su propiedad Align ponla en alTop.
Ahora coloca un componente ShellComboBox, se encuentra en la ficha Samples, en su propiedad Root ponle C:\.
Coloca un componente ShellListView el cual también se encuentra en la ficha Samples, colócalo debajo del ShellComboBox, su propiedad ViewStyle ponla en vsReport, dale click en su propiedad Root ponle c:\ esto con la finalidad de que nos muestre por defecto el contenido de el directorio raíz C. Puedes dejarlo como sale por defecto eso no es tan importante. En la propiedad ShellComboBox elige ShellComboBox1, por medio de esto estaremos ligando estos dos componentes de tal forma que cuando se cambie de ruta cualquiera de los dos, se actualicen automáticamente, etc.
Ahora coloca dos Labels, las cuales dirán Directorios, Archivos, debajo de cada una de ellas pondrás dos componentes ListBox los cuales están en la ficha Standard, quedando el uno para Directorio y el dos para Archivos.
Muy bien, ahora coloca otro componente Listbox, pero ponlo fuera del Panel, en la ultima parte del form que te queda libre, su propiedad Align ponla en alClient.
Coloca un componente StatusBar de la ficha Win32.
Ahora coloca dos componentes PopupMenu, vas crear las siguientes opciones dentro de ellos (Si no sabes como consulta el manual que puse en este Sitio).
Opciones del PopupMenu1:
Descargar.
Renombrar.
Eliminar
Ahora que ya tienes hecho este menú, hay que ligar el PopupMenu1 al ListBox2, así es que ve al ListBox2 y en su propiedad PopupMenu elige PopupMenu1.
Opciones del PopupMenu2:
Renombrar Directorio.
Eliminar Directorio.
Crear Directorio.
Ahora que ya tienes hecho este menú, hay que ligar el PopupMenu2 al ListBox1, así es que ve al ListBox1 y en su propiedad PopupMenu elige PopupMenu2.
Puedes llegar a utilizar un solo menú para los dos ListBox, pero en esta ocasión para mayor facilidad la haremos así.
Por ultimo coloca un componente NMFTP1. y listo hemos terminado el diseño de nuestro formulario.
Ahora bien lo mas interesante el código, mismo que te lo listare a continuación:
He desarrollado un procedimiento propio, para actualizar directorios cada que se necesite, así es que debemos de declararlo, si no sabes no te preocupes es fácil.
Ve al código de tu Form, al inicio de tu código quiero que declares el procedimiento procedure Directorios; A continuación te muestro la parte en la que lo clararas así es que solo localizala y listo, esta al inicio de tu Unit.
procedure
Directorios;
procedure ListBox1DblClick(Sender: TObject);
procedure ListBox2Click(Sender: TObject);
procedure ListBox2DblClick(Sender: TObject);
Ahora que hemos declarado el procedure, solo falta desarrollarlo, en lo particular me gusta desarrollar los procedures propios antes de cualquier otro hecho por el Delphi. así es que vete a la parte en donde dice:
implementation
{$R *.dfm}
Ahora teclea todo el código:
procedure TForm1.Directorios;
var i: Integer;
begin
Memo1.Lines.Add('Generando lista de Archivos y Directorios . . .');
ListBox1.Items.Clear;
ListBox1.Items.Add( '..' );
ListBox2.Items.Clear;
NMFTP1.ParseList := True;
NMFTP1.Vendor := NMOS_AUTO;
NMFTP1.List;
for i := 0 to NMFTP1.FTPDirectoryList.Name.Count - 1 do
if NMFTP1.FTPDirectoryList.Attribute[i][1] = 'd' then
ListBox1.Items.Add( NMFTP1.FTPDirectoryList.Name[i] )
else
ListBox2.Items.Add( NMFTP1.FTPDirectoryList.Name[i] );
Memo1.Lines.Add('Lista Terminada . . .');
end;
Ahora iremos a escribir el código de cada evento de los componentes:
Evento Onclose del Form:
procedure TForm1.FormClose(Sender:
TObject; var Action: TCloseAction);
begin
if NMFTP1.Connected then
NMFTP1.Disconnect;
end;
Código del Botón Conectar:
procedure TForm1.SpeedButton1Click(Sender:
TObject);
begin
NMFTP1.Host := Edit1.Text; //Asignamos el Host
NMFTP1.Port := StrToInt(Edit4.Text); //Se Asgina
el Puerto
NMFTP1.Timeout := 5000; //Sedefine el
Tiempo de Espera
NMFTP1.UserID := Edit2.Text; //Se pone el nombre
de usuario
NMFTP1.Password := Edit3.Text; //Se pone
la Contraseña
Try
begin
NMFTP1.Connect; //Conectamos al servidor
Directorios; //Si no sucede ningún error al
conectar se genera lista de Archivos y Directorios.
end
Except
Memo1.Lines.Add('Error duarnte la conexion'); // Pega
en el memo un mensaje de error.
end;
end;
Código del Botón Desconectar:
procedure TForm1.SpeedButton2Click(Sender:
TObject);
begin
if NMFTP1.Connected then //Si esta conectado se
desconecta del servidor.
NMFTP1.Disconnect;
end;
Código del Botón Cargar:
procedure TForm1.SpeedButton3Click(Sender:
TObject);
begin
if ShellListView1.SelectedFolder.IsFolder = False then {verificamos que
sea un Archivo y no Carpeta.}
begin
Memo1.Lines.Add('Cargando Archivo a Directorio Remoto . . .');
ListBox3.Items.Add('Carga.... ' + ShellListView1.SelectedFolder.PathName);
try
begin
NMFTP1.Upload(ShellListView1.SelectedFolder.PathName,''); {Esta línea es
la que carga el Archivo.}
Directorios; //Despues de la carga se Actualizan los Archivos.
Memo1.Lines.Add('La carga del Archivo ha terminado . . .');
end;
except
Memo1.Lines.Add('Ocurrio un error durante la carga. Intente de Nuevo . . .');
StatusBar1.SimpleText := 'Error en la Carga . . .';
end;
end;
end;
Código del Botón Descargar:
Form1.ListBox2DblClick(Sender); {En este caso el código esta en el evento DblClick del ListBox2, y para no escribirlo dos veces solo lo mando llamar}
Evento OnDblClick del ListBox1:
procedure TForm1.ListBox1DblClick(Sender:
TObject);
begin
Memo1.Lines.Add('Cambiando al directorio ' + ListBox1.Items[ListBox1.ItemIndex]);
NMFTP1.ChangeDir(ListBox1.Items[ListBox1.ItemIndex] ); //Cambia de rirectorio
Directorios;
end; { al hacer Docle Click sobre un directorio
debe acceder a el y mostrar su contenido }
Evento OnClick del ListBox2:
procedure TForm1.ListBox2Click(Sender:
TObject);
begin
StatusBar1.SimpleText := 'Tamaño: ' +
FormatFloat( '###,###,###,##0',
StrToFloat( NMFTP1.FTPDirectoryList.Size[ListBox2.ItemIndex] ) ) + ' bytes ' +
'Fecha: ' + NMFTP1.FTPDirectoryList.ModifDate[ListBox2.ItemIndex];
end; { en este evento pegamos en el StatusBar el nombre y atributos del
archivo}
Evento OnDblClick del ListBox2:
procedure TForm1.ListBox2DblClick(Sender:
TObject);
Var Ruta : String;
begin
if (ListBox2.Count > 0) then //Verifico que no este vacio
begin
Ruta := ShellComboBox1.Path + '\';
ListBox3.Items.Add('Descarga.... ' + ListBox2.Items[ListBox2.ItemIndex]);
Ruta := Ruta + ListBox2.Items[ListBox2.ItemIndex];
NMFTP1.Mode( MODE_IMAGE );
Try
begin
NMFTP1.Download( ListBox2.Items[ListBox2.ItemIndex],Ruta); //Descarga el
Archivo.
Memo1.Lines.Add('Descarga Terminada . . .');
StatusBar1.SimpleText := 'Descarga Terminada . . .';
end
Except
Memo1.Lines.Add('Error duarnte la Descarga . . .');
if Application.MessageBox('Error al descargar, Desea Resumir la descarga
?','DESCARGA',MB_OKCANCEL+MB_ICONQUESTION) = IDOK then
begin
Memo1.Lines.Add('Resumiendo la Descarga Fallida . . .');
NMFTP1.DownloadRestore( ListBox2.Items[ListBox2.ItemIndex],Ruta);
//Resume la Descarga
Memo1.Lines.Add('Descarga Terminada . . .');
StatusBar1.SimpleText := 'Descarga Terminada . . .';
end;
end;
end;
end;
{ En este procedimiento utilizo la variable Ruta en la cual genero la ruta donde
se guardara el archivo. Las descargas son susceptibles a errores, es por eso que
con el Try y el Except controlo y me doy cuenta si se genero un error, para en
caso de que se genere un error resumir la descarga y no tener que empezar desde
el principio, claro si es que el servidor soporta los Restore.}
Opción Descargar del PopupMenu1:
procedure TForm1.DEscargar1Click(Sender:
TObject);
begin
Form1.ListBox2DblClick(Sender);
end;
Opción Renombrar del PopupMenu1:
procedure TForm1.Renombrar1Click(Sender:
TObject);
var
Actual, Anterior : String;
begin
if (ListBox2.Count > 0) then
begin
Anterior := ListBox2.Items[ListBox2.ItemIndex];
if InputQuery('Renombrar Archivo', 'Nuebo Nombre ?', Actual) then
begin
NMFTP1.Rename(Anterior,Actual);
Memo1.Lines.Add('Archivo Renombrado . . .');
Directorios;
end;
end;
end;
Opción Eliminar del PopupMenu1:
procedure TForm1.Eliminar1Click(Sender:
TObject);
var Archivo : String;
begin
if (ListBox2.Count > 0) then
begin
Archivo := ListBox2.Items[ListBox2.ItemIndex];
if Application.MessageBox('Esta seguro de Eliminar el Archivo ?','ELIMINAR',MB_OKCANCEL+MB_ICONQUESTION)
= IDOK then
begin
NMFTP1.Delete(Archivo);
Memo1.Lines.Add('Archivo eliminado . . .');
Directorios;
end;
end;
end;
Opción Renombrar Directorio del PopupMenu2:
procedure TForm1.RenombrarDirectorio1Click(Sender:
TObject);
var Anterior, Actual : String;
begin
if (ListBox1.Count > 1) then
begin
Anterior := ListBox1.Items[ListBox1.ItemIndex];
if InputQuery('Renombrar Directorio', 'Nuebo Nombre ?', Actual) then
begin
NMFTP1.Rename(Anterior,Actual);
Memo1.Lines.Add('Directorio Renombrado . . .');
Directorios;
end;
end;
end;
Opción Eliminar Directorio del PopupMenu2:
procedure TForm1.EliminarDirectorio1Click(Sender:
TObject);
Var Dir : String;
begin
if (ListBox1.Count > 0) then
begin
Dir := ListBox1.Items[ListBox1.ItemIndex];
if Application.MessageBox('Esta seguro de Eliminar el Directorio ?','ELIMINAR',MB_OKCANCEL+MB_ICONQUESTION)
= IDOK then
begin
NMFTP1.RemoveDir(Dir);
Memo1.Lines.Add('Directorio Eliminado . . .');
Directorios;
End;
end;
end;
Opción Crear Directorio del PopupMenu2:
procedure TForm1.CrearDirectorio1Click(Sender:
TObject);
var Dir : String;
begin
if NMFTP1.Connected then
begin
if InputQuery('Crear Directorio', 'Nombre Directorio ?',Dir) then
begin
NMFTP1.MakeDirectory(Dir);
Memo1.Lines.Add('Directorio Creado . . .');
Directorios;
end;
end;
end;
Evento OnAuthenticationFailed del NMFTP1:
procedure TForm1.NMFTP1AuthenticationFailed(var
Handled: Boolean);
begin
Memo1.Lines.Add('Pasword Incorrecto, Intente de nuevo');
end;
Evento OnConnect del NMFTP1:
procedure TForm1.NMFTP1Connect(Sender:
TObject);
begin
Memo1.Lines.Add('Conexion establecida');
Directorios;
end;
Evento OnConnectionFailed del NMFTP1:
procedure TForm1.NMFTP1ConnectionFailed(Sender:
TObject);
begin
Memo1.Lines.Add('No se puede conectar con el Sitio.');
end;
Evento OnDisconnet del NMFTP1:
procedure TForm1.NMFTP1Disconnect(Sender:
TObject);
begin
Memo1.Lines.Add('Desconectado del Sitio . . .')
end;
Evento OnHostResolved del NMFTP1:
procedure TForm1.NMFTP1HostResolved(Sender:
TComponent);
begin
Memo1.Lines.Add('EL Host esta atendiendo la petición . . .');
end;
Evento OnPacketRecvd del NMFTP1:
procedure TForm1.NMFTP1PacketRecvd(Sender:
TObject);
begin
StatusBar1.SimpleText := 'Descargando: ' + ListBox2.Items[ListBox2.ItemIndex]+ '
' + IntToStr(NMFTP1.BytesRecvd)+' of '+IntToStr(NMFTP1.BytesTotal);
end; //se genera en Status Bar el progreso.
EventoOnPacketSent del NMFTP1:
procedure TForm1.NMFTP1PacketSent(Sender:
TObject);
begin
StatusBar1.SimpleText := 'Cargando: ' + ShellListView1.SelectedFolder.PathName +
' ' + IntToStr(NMFTP1.BytesRecvd) + ' of '+IntToStr(NMFTP1.BytesTotal);
end;
Con esto hemos concluido de construir nuestro Cliente FTP. Si te das cuenta con solo un poco mas de cosas quedaría perfectamente bien, ya que lo único que le haría falta a parte de mas validaciones seria que te permitiera copiar las carpetas enteras y con múltiples archivos.
Bueno como ya se esta haciendo tradición en mi sitio, les dejo el código fuente del Cliente FTP. Si tienes algún comentario bueno o malo házmelo saber, recuerda que mi intención solo es ayudar y conociendo sus opiniones lo podré hacer mucho mejor.
Si tienes algún comentario o deseas compartir alguna información da Click Aquí.....