zoukankan      html  css  js  c++  java
  • 用IDFTP开发ftp客户端

    (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;

  • 相关阅读:
    【IO流】FileInputStream FileOutputStream BufferInputStream BufferOutputStream
    [集合]集合相关简单总结
    [集合]Map的 entrySet() 详解以及用法(四种遍历map的方式)
    [集合]HashMap和Hashtable区别
    [集合]Map
    [集合Set]HashSet、LinkedHashSet TreeSet
    IOS UIView 之实例方法篇 Instance Methods
    css 让一张彩色的图片变成一张黑白图
    窗口改变的时候自动刷新浏览器
    php 交互式命令行工具
  • 原文地址:https://www.cnblogs.com/djcsch2001/p/1806466.html
Copyright © 2011-2022 走看看