Solved

Indy IdFTP DOWNLOAD ENTIRE FOLDER

Posted on 2004-10-27
4,296 Views
Last Modified: 2008-01-09
Hi, my problem is about a specific function of the Indy IdFTP component (version 9, used with delphi 7):

I need to download from an ftp site an entire forlder, with all its content, and recreate obviously on local pc the exact directory structure. I've tried with a recursive function, but the function does't work properly.

Here the code:

procedure TFRM_FTPCLIENT.DownloadFolder(Source, Dest :string; bRootFlag : boolean);
var
  i : integer;
  LS : TStringList;

begin

    if bRootFlag = True then //First Call
        begin
        g_sRootDir := StringReplace(GetStringToken(Source, '/', -1), '/', '\', [rfReplaceAll]); //I Obtain the root directory to create locally
        CreateDir(Dest + '\' + g_sRootDir);
        Dest := Dest + '\' + g_sRootDir;
        bRootFlag := False;
        end
    else
        begin
        CreateDir(Dest);
        end;

    IdFTP1.ChangeDir(Source); //Enter in the dir
    LS:= TStringList.Create; //make the list of all files and dirs
    IdFTP1.List(LS); //Fill the stringlist with all founded files and dirs

    for i := 0 to LS.Count - 1 do
        begin
        FRM_FTPCLIENT.Update;
        Application.ProcessMessages;

        With IdFTP1.DirectoryListing.Items[i] do
            begin
            if ItemType = ditDirectory then
                begin
                Dest := Dest + '\' + FileName;
                DownloadFolder(Source + '/' + FileName, Dest, False); //recursive call
                end
            else
                begin
                IdFTP1.Get(Source + '/' + FileName, Dest + '\' + FileName, true);
                end;
            end;
           
        end;

end;


This procedure fails when the cicle enter in a folder where there are only files and then stop with an exception of "List index out of bounds(1)".

I have made with success a similar procedure to UPLOAD a folder, and this procedure works fine. Here the code:


procedure TFRM_FTPCLIENT.UploadFolder(Source, Dest: string);
var F:          TSearchRec;
    i:          integer;
    LongPath:   WideString;
    Data:       TWin32FindDataW;
    Handle:     THandle;
    sFileOrDir, sDestStr, sIndirizzo, sPorta, sNomeUtente, sPassword, sSottoDirectory, sPassiveMode :string;
    sStringListDest : TStringList;
    dtDirDate : TDateTime;
    iDirSize : Integer;
    sRootDir : string;

Begin

if bAbort or g_bAbortFtp or AbortTransfer or (not IdFTP1.Connected) then
    begin
    MessageDlg('Trasferimento interrotto.', mtError, [mbOK], 0);
    Exit;
    end;

try
if iRootFlag = 0 then
    begin
    g_ElementRoot := StringReplace(Source, '\', '/', [rfReplaceAll]);
    g_sRootDir := Dest + '/' + ExtractFileName(Source);
    if not FTP_DirExists(g_sRootDir, dtDirDate, iDirSize, IdFTP1) then
        IdFTP1.MakeDir(g_sRootDir); //Vi crea la cartella root
    //IdFTP1.ChangeDir(g_sRootDir); //Entra nel sottopercorso specificato
    Dest := g_sRootDir;
    end;
if iRootFlag = 1 then
    begin
    Dest := g_sRootDir + GetSubPath(Source); //prendo il sottopercorso
    if not FTP_DirExists(Dest, dtDirDate, iDirSize, IdFTP1) then
        IdFTP1.MakeDir(Dest); //Vi crea la cartella root
    //if FRM_PRINCIPALE.IdFTP1.RetrieveCurrentDir <> sSottoDirectory then
        //FRM_PRINCIPALE.IdFTP1.ChangeDir(sSottoDirectory); //Entra nel sottopercorso specificato
    end;
except on e:exception do

end;

iRootFlag := 1;

    i := Findfirst(Source + '\*.*', faAnyFile, F);

    if F.Name <> '.' then
      begin
      i := FindNext(F); // trova ".."
      i := FindNext(F); // trova il primo file
      end;


    while i = 0 do
        begin

        sFileOrDir := F.Name;

        try
        if bAbort or g_bAbortFtp or AbortTransfer or (not IdFTP1.Connected) then Exit;

          if sFileOrDir <> '.' then
            if sFileOrDir <> '..' then

                if F.Attr And faDirectory > 0 then  //If is a directory
                    UploadFolder(Source + '\' + sFileOrDir, Dest)
                else    //else if is a file
                    UploadFile(Source + '\' + sFileOrDir, Dest);

        i := FindNext(F);
        except on E:Exception do
            begin
            i := FindNext(F);
            continue;
            end
        end;
        end;

    FindClose(F);

end;


Can anyone help me ?


0
Question by:enumaelis
    4 Comments
     
    LVL 26

    Accepted Solution

    by:
    Taka look at my FTPDirToTreeView tip here:
    http://www.delphipages.com/tips/thread.cfm?ID=210

    It should give you an idea of how to do it.
    0
     
    LVL 11

    Expert Comment

    by:calinutz
    0
     
    LVL 17

    Expert Comment

    by:geobul
    Hi,

    The function fails when it gets back from the most inner recursive call (i.e. in the previous one) because you're using the same IdFTP component filling its DirectoryListing.Items in on every call. When your most inner call finishes this property contains the values from that folder and doesn't contain the values from the previous one anymore. Thus, your loop fails executing the line

    With IdFTP1.DirectoryListing.Items[i]

    or even worse it could get files from the inner remote folder into the outher local one (when the number of the entries in the inner folder is bigger or equal).

    The solution is to create a local copy of IdFTP1.DirectoryListing.Items alter List and before the loop. And use that copy in the loop instead of IdFTP1. Something like (not tested):

    var
      FolderItems: TIdFTPListItems;
    ...
      IdFTP1.List(LS); //Fill the stringlist with all founded files and dirs
      FolderItems := TIdFTPListItems.Create;
      FolderItems.LoadList(LS);

      for i := 0 to LS.Count - 1 do
            begin
            FRM_FTPCLIENT.Update;
            Application.ProcessMessages;

            With FolderItems.Items[i] do
    ...


    And don't forget to free LS and FolderItems at the end of the procedure.

    Regards, Geo
    0
     

    Author Comment

    by:enumaelis
    Thanks to Eddie ShipMan! :-) :-)

    I've modified his procedure like follow:


    procedure TFRM_FTPCLIENT.DownloadFolder(AFTP: TIdFTP; const Source:string; Dest:string; bRootFlag:Boolean);
    var
     TempItem: TTreeNode;
     I: Integer;
     DirList: TIdFTPListItems;
     DirItem: TIdFTPListItem;
     LS: TStringList;

        function InvertiBarre(sSource:string) : string;
            begin
            Result := StringReplace(sSource, '/', '\', [rfReplaceAll])
            end;

    begin


    if bRootFlag = True then
        begin
        g_sRootDir := StringReplace(GetStringToken(Source, '/', -1), '/', '\', [rfReplaceAll]);
        CreateDir(Dest + '\' + g_sRootDir);
        Dest := Dest + '\' + g_sRootDir;
        bRootFlag := False;
        end
    else
        begin
        CreateDir(Dest);
        end;


     LS := TStringList.Create;

     try
       LS.Sorted := True;

       try
         if (Source <> '') then
           AFTP.ChangeDir(Source);

         AFTP.TransferType := ftASCII;
         AFTP.List(nil);

         DirList := AFTP.DirectoryListing;
         for i := 0 to DirList.Count - 1 do
            begin
            try
                DirItem := DirList.Items[i];
                if (DirItem.ItemType = ditDirectory) then
                    begin
                    LS.Add(Trim(DirItem.FileName) + '/');
                    end
                else
                    begin
                    AFTP.TransferType := ftBinary;
                    DownloadFile(DirItem.FileName, Dest);
                    end;
            except
            end;//try
            end;//for

         for i := 0 to LS.Count - 1 do
            begin
            DownloadFolder(AFTP, Source + '/' + LS.Strings[i], Dest + '\' + InvertiBarre(LS.Strings[i]), False);
            end;

       finally

       end;
     finally
       LS.Free;
     end;
     
    end;



    and it works !!!! :-) :-)
    0

    Write Comment

    Please enter a first name

    Please enter a last name

    We will never share this with anyone.

    Featured Post

    What Security Threats Are You Missing?

    Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

    A lot of questions regard threads in Delphi.   One of the more specific questions is how to show progress of the thread.   Updating a progressbar from inside a thread is a mistake. A solution to this would be to send a synchronized message to the…
    Hello everybody This Article will show you how to validate number with TEdit control, What's the TEdit control? TEdit is a standard Windows edit control on a form, it allows to user to write, read and copy/paste single line of text. Usua…
    This video discusses moving either the default database or any database to a new volume.
    Excel styles will make formatting consistent and let you apply and change formatting faster. In this tutorial, you'll learn how to use Excel's built-in styles, how to modify styles, and how to create your own. You'll also learn how to use your custo…

    913 members asked questions and received personalized solutions in the past 7 days.

    Join the community of 500,000 technology professionals and ask your questions.

    Join & Ask a Question

    Need Help in Real-Time?

    Connect with top rated Experts

    15 Experts available now in Live!

    Get 1:1 Help Now