Want to win a PS4? Go Premium and enter to win our High-Tech Treats giveaway. Enter to Win

x
?
Solved

Indy IdFTP DOWNLOAD ENTIRE FOLDER

Posted on 2004-10-27
4
Medium Priority
?
4,537 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
Comment
Question by:enumaelis
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
4 Comments
 
LVL 26

Accepted Solution

by:
EddieShipman earned 750 total points
ID: 12424178
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
ID: 12430531
0
 
LVL 17

Expert Comment

by:geobul
ID: 12431208
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
ID: 12493789
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

Featured Post

Hire Technology Freelancers with Gigs

Work with freelancers specializing in everything from database administration to programming, who have proven themselves as experts in their field. Hire the best, collaborate easily, pay securely, and get projects done right.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Visualize your data even better in Access queries. Given a date and a value, this lesson shows how to compare that value with the previous value, calculate the difference, and display a circle if the value is the same, an up triangle if it increased…
Are you ready to place your question in front of subject-matter experts for more timely responses? With the release of Priority Question, Premium Members, Team Accounts and Qualified Experts can now identify the emergent level of their issue, signal…
Suggested Courses

597 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