Indy IdFTP DOWNLOAD ENTIRE FOLDER

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 ?


enumaelisAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

Eddie ShipmanAll-around developerCommented:
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

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
calinutzCommented:
0
geobulCommented:
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
enumaelisAuthor Commented:
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
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.