(1)控件属性设置
RecvBufferSize说明(默认值为8192字节):该属性为整型变量,用于指定连接所用的接受缓冲区大小。
这些属性是indyftp里面的。
SendBufferSize说明(默认值为32768字节):该属性也为整型变量,用于指定连接所用的发送缓冲区的最大
值。该属性在WriteStream方法中 时,可用于TStream指定要发送内容的块数。如果要发送的内容大于本属性
值,则发送内容被分为多个块发送。
TransferType说明(默认值为ftBinary):该属性为TIdFTPTransferType型变量。用于指定传输内容是二进制
文件(ftBinary )还是ASCII文 件(ftASCII)。应用程序需要使用二进制方式传输可执行文件、压缩文件和
多媒体文件等;而使用ASCII方式传输文本或超文本等文本型数据 。
(2)控件的事件响应
OnDisconnected响应:TNotifyEvent类,用于响应断开(disconnect)事件。当Disconnect方法被调用用来关
闭Socket的时候,触发该响应。 应用程序必须指定该事件响应的过程,以便对该断开事件进行相应。
OnStatus响应:TIdStatusEvent类。该响应在当前连接的状态变化时被触发。该事件可由DoStatus方法触发并提供给事件控制器属性。 axStatus是当前连接的TIdStatus值;aaArgs是一个可选的参数用于格式化函数,它将用于构造表现当前连接状态的文本消息。
OnWork响应:OnWord是TWorkEvent类事件的响应控制器。OnWork用于关联DoWork方法当缓冲区读写操作被调用时通知Indy组件和类。它一般被 用于控制进度条和视窗元素的更新。AWorkMode表示当前操作的模式,其中:wmRead-组件正在读取数据;wmWrite-组件正在发送数据。 AWorkCount指示当前操作的字节计数。
OnWorkBegin响应:TWorkBeginEvent类。当缓冲区读写操作初始化时,该事件关联BeginWork方法用于通知Indy组件和类。它一般被用于控制进 度条和视窗元素的更新。AWorkMode表示当前操作的模式,其中:wmRead-组件正在读取数据;wmWrite-组件正在发送数据。AWorkCountMax用于 指示发送到OnWorkBegin事件的操作的最大字节数,0值代表未知。
OnWorkEnd响应:TWorkEndEvent类。当缓冲区读写操作终止时,该事件关联EndWork方法用于通知Indy组件和类。AWorkMode表示当前操作的模式,其中:wmRead-组件正在读取数据;wmWrite-组件正在发送数据。AWorkCount表示操作的字节数。
在一般情况下,在OnDisconnected中设定连接断开的界面通知;在OnStatus中设定当前操作的状态;在OnWork中实现传输中状态条和其他参数 的显示;而在OnWorkBegin和OnWorkEnd中分别设定开始传输和传输结束时的界面。
好,下面是用到的 IdLogEvent的介绍。
事件:onReceived 就是已经接受到数据后应该做的事,而onReceive就是在接受过程中想做的事。
同理,onSent和onSend也是一样的道理。一般用onReceived和onSent.
好,下面是FTP客户端的实现了。其实FTP实现起来很简单,只要放IdFTP到界面上,然后进行连接后,就可以进行上传和下载了。当然,服务器要支持上传才行。
1在form的create写 IdFTPClient.Intercept :=IdLogEvent1;用于捕获Indy发送和接收的数据,在onreceived和onsent里面写如下代码:
procedure TFtpForm.IdLogEvent1Received
(ASender: TComponent; const AText,AData: String);
begin
ConnInfo('<<- ',AData);
end;
procedure TFtpForm.IdLogEvent1Sent(ASender: TComponent; const AText,AData: String);
begin
ConnInfo('->> ',AData);
end;
接收和发送数据时会把信息加到listbox里面。
ConnInfo是连接的信息,实现如下:
procedure TFtpForm.ConnInfo(Operation, S1: String);
Var
S: String;
begin
while Length(S1) > 0 do begin
if Pos(#13, S1) > 0 then begin
S := Copy(S1, 1, Pos(#13, S1) - 1);
Delete(S1, 1, Pos(#13, S1));
if S1[1] = #10 then Delete(S1, 1, 1);
end
else
S := S1;
ListBoxInfo.ItemIndex := ListBoxInfo.Items.Add(Operation + S);
end;
end;
然后写上程序的异常处理,我用的是ApplicationEvent组件
在ApplicationEvent的onexception里面写入WriteLog(E);把出现的异常记录在日志中。writelog的实现如下:
procedure TFTPForm.WriteLog(E:Exception);
const
FileNameExt = '.log';
Path = 'log/' ;
var
F:TextFile;
DateStr,FileName,Buf:string;
begin
DateTimeToString(DateStr,'yyyy-mm-dd',Now());
FileName := Path+DateStr + FileNameExt;
AssignFile(F,FileName);
if FileExists(FileName) then
Append(F)
else
Rewrite(F);
try
DateTimeToString(Buf,'yyyy-mm-dd hh:nn:ss:',Now());
Buf := Buf+E.Message;
Writeln(F,Buf);
Application.ShowException(E);
finally
CloseFile(F);
end;
end;
把产生的异常按日期生成文件。如果文件存在,则把异常写入,如果不存在,则创建文件。
好,外围的工作已经做好了,到了idftp的工作了。
连接FTP服务器端FTP客户端名为IdFTPClient设定FTP的服务器地址和端口,用户名和密码后就可以连接了。如果成功了,在listbox里面显示连接成功。
IdFTPClient.Host := Trim(EdtHost.Text);
IdFTPClient.Port := StrToInt(Trim(EdtPort.Text));
IdFTPClient.Username := Trim(EdtName.Text);
IdFTPClient.Password := Trim(EdtPwd.Text);
ListBoxInfo.Items.Add('连接到:'+EdtHost.Text);
IdFTPClient.Connect();
ListBoxInfo.Items.Add('登陆成功');
在IdFTP的OnStatus里面写 ListBoxInfo.ItemIndex := ListBoxInfo.Items.Add(aStatusText);显示连接的状态,并且此时会触发IdLogEvent的onReceived和onSent事件。
在连接了服务器后,需显示服务器端的文件,用下面的过程,不用IdFTP自带的List:
procedure TFtpForm.ViewRemoteList(Dir:string);
var
DirList:TStringList;
i:Integer;
ListItem:TListItem;
Attribute:TIdDirItemType;
begin
DirList := TStringList.Create;
try
try
IdFTPclient.ChangeDir(Dir);
IdFTPClient.List(DirList);
EdtDir.Text := IdFTPClient.RetrieveCurrentDir;
except on E:Exception do
begin
WriteLog(E);
end;
end;
finally
DirList.Free;
end;
RemoteListView.Clear;
for i:=0 to IdFTPClient.DirectoryListing.Count-1 do
begin
ListItem := RemoteListView.Items.Add();
ListItem.Caption := IdFTPClient.DirectoryListing.FileName;
ListItem.SubItems.Add(IntToStr(IdFTPClient.DirectoryListing.Size));
ListItem.SubItems.Add(DateToStr(IdFTPClient.DirectoryListing.ModifiedDate));
Attribute := IdFTPClient.DirectoryListing.ItemType;
if Attribute=ditDirectory then
begin
ListItem.SubItems.Add('目录');
ListItem.ImageIndex := 0;
end
else if Attribute=ditFile then
begin
ListItem.SubItems.Add('文件');
ListItem.ImageIndex := 1;
end
else if Attribute=ditSymbolicLink then
begin
ListItem.SubItems.Add('其他');
ListItem.ImageIndex := 2;
end;
end;
end;
断开FTP服务器连接 IdFTPClient.Disconnect();
好了,到了关键的上传和下载功能了。
上传功能,如果是上传文件的话:直接用idftp.put(....)就行了。
如果是上传文件夹的话,情况就复杂很多了。
if ShellTreeView1.SelectedFolder=nil then
begin
Messagebox(Handle,' 错误,没有选择文件夹/文件!','FTP客户端',MB_ICONERROR+MB_OK);
Exit;
end;//看有没有选择文件
FNameLs := TStringList.Create;
for i:=0 to IdFTPClient.DirectoryListing.Count-1 do
begin
FNameLs.Add(IdFTPClient.DirectoryListing.Items.FileName);
Application.ProcessMessages;
end;//遍历FTP服务器端的文件,把所有文件的名称保存在 FNameLs里面,
//判断服务器端是否有同名的文件或文件夹,如果存在的话,提示是否覆盖,如果是的话,就删除文件/文件夹
for i:=0 to FNameLs.Count-1 do
begin
if FNameLs <> ShellTreeView1.SelectedFolder.DisplayName then
continue
else
begin
if Application.MessageBox('目录/文件已存在,是否替换?','提示',MB_OKCANCEL+MB_ICONQUESTION)=IDOK then
begin
if IdFTPClient.DirectoryListing.Items.ItemType=ditDirectory then
try
DelDir(IdFTPClient,FNameLs,FNameLs,IdFTPClient.RetrieveCurrentDir);
finally
end
else
try
IdFTPClient.Delete(FNameLs);
finally
end;
end
else
Exit;
end;
end;
//判断是否是文件夹,是的话,首先获取文件夹的大小
if ShellTreeView1.SelectedFolder.IsFolder then
FileSize := GetDirectorySize(ShellTreeView1.SelectedFolder.PathName);//然后上传文件夹
UpLoadDir(IdFTPClient.RetrieveCurrentDir,ShellTreeView1.SelectedFolder.PathName);
//获得文件夹大小 和 上传的函数如下:
function TFTPForm.GetDirectorySize(ADir: string): Integer;
var
Dir: TSearchRec;
Ret: integer;
Path: string;
begin
Result := 0;
Path := ExtractFilePath(ADir);
Ret := FindFirst(ADir, faAnyFile, Dir);
if Ret <> NO_ERROR then exit;
try
while ret = NO_ERROR do
begin
inc(Result, Dir.Size);
if (Dir.Attr in [faDirectory]) and (Dir.Name[1] <> '.') then
Inc(Result, GetDirectorySize(Path + Dir.Name + '\*.*'));
Ret := FindNext(Dir);
end;
finally
FindClose(Dir);
end;
end;
//参考网上的资料
//思路:传进来的有remotepath,表示是FTP服务器端的当前目录,localpath是想上传的文件名
根据上传文件名,在当前目录下创建文件夹,然后循环查找文件夹里面是否还有文件夹,有的话的继续创建,就是把文件夹全部创建出来。
所有的文件夹创建出来后,在一个个的上传文件。
procedure TFtpForm.UpLoadDir(RemotePath,LocalPath:string);
var
strl1,strl2:TStringList;
sr: TSearchRec;
i,j,DirCount,FileCount:integer;
begin
IdFTPClient.ChangeDir(RemotePath);
DirCount := 0;
FileCount := 0;
IdFTPClient.MakeDir(Copy(LocalPath,LastDelimiter('\',LocalPath)+1,Length(LocalPath)));
//创建文件夹
//功能就是把当前文件夹里面的所有文件夹名称找出来
if FindFirst(LocalPath+'\*.*',faDirectory,sr)=0 then //查找想上传的文件夹里面是否还有文件夹
begin
strl1 := TStringList.Create;
repeat
if(sr.Attr=faDirectory) and (sr.Name<>'.') and (sr.Name<>'..') then //如果是文件夹,并且名字不是'.'或者'..'
begin
strl1.Add(sr.Name);
Inc(DirCount);
end;
until FindNext(sr) <> 0;
FindClose(sr);
end;
for i:=0 to DirCount-1 do
begin
UpLoadDir(RemotePath+'/'+Copy(LocalPath,LastDelimiter('\',LocalPath)+1,Length(LocalPath)),LocalPath+'\'+strl1.Strings );
end;
//找出所有的文件
if FindFirst(LocalPath+'\*.*',faAnyFile,sr)=0 then
begin
strl2 := TStringList.Create;
repeat
if(sr.Attr<>faDirectory) then
begin
strl2.Add(sr.Name);
Inc(FileCount);
end;
until FindNext(sr)<>0;
FindClose(sr);
end;
IdFTPClient.ChangeDir(RemotePath+'/'+Copy(LocalPath,LastDelimiter('\',LocalPath)+1,Length(LocalPath)));
for j:=0 to FileCount-1 do
begin
try
IdFTPClient.Put(LocalPath+'\'+strl2[j],IdFTPClient.RetrieveCurrentDir+'/'+strl2[j]);
Application.ProcessMessages;
ListBoxInfo.Items.Add('上传成功!');
except on E:Exception do
begin
ListBoxInfo.Items.Add('上传失败!');
WriteLog(E);
continue;
end;
end;
end;
end;
//如果上传的是文件,取得文件的大小,取得大小主要是显示进度条。
if not ShellTreeView1.SelectedFolder.IsFolder then
FileSize := FileSizeByName(ShellTreeView1.SelectedFolder.PathName);//上传文件
IdFTPClient.Put (ShellTreeView1.SelectedFolder.PathName,IdFTPClient.RetrieveCurrentDir+'/'+ShellTreeView1.SelectedFolder.DisplayName);
文件/文件夹下载的步骤也是相似的。 文件的下载:
IdFTPClient.Get(Name,ShellTreeView1.Folders[ShellTreeView1.Selected.Index].PathName+'\'+Name,false,true);
文件夹的下载,都是用递归实现:
procedure TFTPForm.DownloadDir(var idFTP : TIdFtp;RemoteDir,LocalDir : string);
var
i,DirCount : integer;
begin
if not DirectoryExists(LocalDir + RemoteDir) then
ForceDirectories(LocalDir+'\' + RemoteDir);
idFTP.ChangeDir(RemoteDir);
idFTP.List(nil);
DirCount := idFTP.DirectoryListing.Count ;//获取所有文件或者文件夹的数量
if DirCount = 0 then
begin
idFTP.ChangeDirUp;
idFTP.List(nil);
Exit;
end;
for i := 0 to DirCount - 1 do
begin
if DirCount <> idFTP.DirectoryListing.Count then
begin
repeat
idFTP.ChangeDirUp;
idFTP.List(nil);
Application.ProcessMessages;
until DirCount = idFTP.DirectoryListing.Count ;
end;
if idFTP.DirectoryListing.ItemType = ditDirectory then //如果要下载的是文件夹 DownloadDir(idFTP,idFTP.DirectoryListing.FileName,LocalDir + RemoteDir + '\')
else
begin
try
idFTP.Get(idFTP.DirectoryListing.FileName,LocalDir + RemoteDir + '\' +
idFTP.DirectoryListing.FileName,true); //下载文件
except on E:Exception do
begin
ListBoxInfo.Items.Add('文件下载出错!');
WriteLog(E);
end;
end;
Application.ProcessMessages;
if i = DirCount - 1 then
begin
idFTP.ChangeDirUp;
idFTP.List(nil);
end;
end;
end;
end;
//删除功能
文件的删除很简单,直接IdFTPClient.Delete(Name)就行了
procedure TFTPForm.DelDir(var idFTP : TIdFtp;RemoteDir,RootDir : string;const Path:string);
label Files;
var
i,DirCount : integer;
Temp,Temp2 : string;
begin
idFTP.ChangeDir(RemoteDir);
if Pos(RootDir,idFTP.RetrieveCurrentDir) = 0 then Exit;
Files :
idFTP.List(nil);
DirCount := idFTP.DirectoryListing.Count ;
while DirCount = 0 do
begin
Temp := idFTP.RetrieveCurrentDir;
idFTP.ChangeDirUp;
idFTP.RemoveDir(Temp);
idFTP.List(nil);
Temp2 := idFTP.RetrieveCurrentDir;
if Temp2=Path then Exit;
DirCount := idFTP.DirectoryListing.Count ;
for i := 0 to DirCount - 1 do
if idFTP.DirectoryListing.FileName = RootDir then
Exit;
end;
for i:= 0 to DirCount - 1 do
begin
if Pos(RootDir,idFTP.RetrieveCurrentDir) = 0 then
Break ;
if idFTP.DirectoryListing.ItemType = ditDirectory then
begin
try
DelDir(idFTP,idFTP.DirectoryListing.FileName,RootDir,'0');
Application.ProcessMessages;
except on e:Exception do
WriteLog(E);
end;
end
else
begin
idFTP.Delete(idFTP.DirectoryListing.FileName);
Application.ProcessMessages;
goto Files ;
end;
end;
end;