IDFTP DirExists and MakeDir
Asked Answered
M

3

5

I am using Indy IDFTP to make a directory. I need to find a reliable way to determine if a directory exists and if it does not exist, to call MakeDir. I have tried the following code but an exception did not occur when List was called so MakeDir was not executed even though the directory did not exist at the time. How can I determine if a directory exists and create the directory if it does not exist?

{ Check to see if the '/public_html/XXX/' + iDomain + 'Thumbnails' directory exists }
    try
      IdFTP1.List(nil, '/public_html/XXX/' + iDomain + 'Thumbnails', False);
    except
      on e: EIdReplyRFCError do
      begin
        { '/public_html/XXX/' + iDomain + 'Thumbnails' directory does not exist }
        StatusBar1.SimpleText := 'Making thumbnail directory...';
        StatusBar1.Update;
        iFolder := '/public_html/XXX/' + iDomain;
        { Change directory to /public_html/XXX/iDomain }
        IdFTP1.ChangeDir(iFolder);
        iFolder := 'Thumbnails';
        { Create FTP Directory for Thumbnails }
        IdFTP1.MakeDir(iFolder);
      end;
    end;

During my testing the directory did not exist but at runtime e was nil? Is my approach to this correct?

Minier answered 15/1, 2015 at 0:50 Comment(0)
S
9

If TIdFTP.List() is not raising an exception, the FTP server is most likely returning a 450 reply code, which simply means "Requested file action not taken". TIdFTP.InternalGet() (which is used by TIdFTP.List(), TIdFTP.ExtListDir(), and TIdFTP.Get()) does not treat 450 as an error condition, as some servers (like Ericsson Switch FTP) send 450 when listing the contents of an empty but otherwise existent directory, so there is no listing data to send. Some servers do send 450 when the requested directory does not exist, though. TIdFTP.List() does not try to differentiate. However, if TIdFTP.List() does not raise an exception, you can look at the TIdFTP.LastCmdResult property to differentiate manually if needed.

Also, you can't just rely on the fact that an exception is raised to mean the folder does not exist. Any number of possible errors could occur. You have to actually look at the error and act accordingly, eg:

var
  Exists: Boolean;

try
  IdFTP1.List(nil, '/public_html/XXX/' + iDomain + 'Thumbnails', False);
  Exists := True;
  if IdFTP1.LastCmdResult.NumericCode = 450 then
  begin
    if (IdFTP1.LastCmdResult.Text.Text has a message like 'No such file or directory' or similar) then begin
      Exists := False;
    end;
    // look for other possible text messages...
  end;
except
  on e: EIdReplyRFCError do
  begin
    if (e.ErrorCode <> 550) or (e.Message does not have a message like 'Directory not found' or similar) then begin
      raise;
    end;
    Exists := false;
  end;
end;

if not Exists then
begin
  { '/public_html/XXX/' + iDomain + 'Thumbnails' directory does not exist }
  StatusBar1.SimpleText := 'Making thumbnail directory...';
  StatusBar1.Update;
  iFolder := '/public_html/XXX/' + iDomain;
  { Change directory to /public_html/XXX/iDomain }
  IdFTP1.ChangeDir(iFolder);
  iFolder := 'Thumbnails';
  { Create FTP Directory for Thumbnails }
  IdFTP1.MakeDir(iFolder);
end;

A better way would be to either:

  1. ChangeDir() to the target directory directly and see if it fails. If so, start walking backwards through the path, calling ChangeDir() for each parent directory until one finally succeeds, then walk back up the path using MakeDir() and ChangeDir() to create the missing sub-directories as needed.

  2. Start with the first directory in the path and ChangeDir() to it, then List() it to see if the next sub-folder is present, MakeDir() it if needed, then ChangeDir() to it, and repeat as needed until you reach the target directory.

Welcome to FTP. It is not a very efficient protocol for directory management.

Seldon answered 15/1, 2015 at 1:16 Comment(3)
What is the code if the directory is non-existent with TIdFTP.LastCmdResult? I tried looking this up but did not find anything.Minier
How would you code this part: if (IdFTP1.LastCmdResult.Text.Text has a message like 'No such file or directory' or similar) then beginMinier
The LastCmdResult.Text.Text property is a String. Just do normal String comparisons for any error messages you need to handle. Like I said, error messages can be server-specific, they are not dictated by the FTP spec, and not all servers conform to the messages that are suggested by the spec, so you may have to check for multiple string values.Seldon
R
1

Simple solution is

      IdFTP.ChangeDir('/www/image/catalog/');  // connect to to directory where you want to add folder

      try
        IdFTP.MakeDir(new_Dir_name);          // try to create folder
        IdFTP.ChangeDir('/www/image/catalog/' + new_Dir_name );  // connect to new folder
      except
        IdFTP.ChangeDir('/www/image/catalog/' + new_Dir_name ); // if folder exist just connect to folder which exist
      end;
Rangefinder answered 21/5, 2018 at 17:5 Comment(0)
A
0

Here is how to upload a folder. Source.

{
  Upload the LocalDir to an FTP server, in the RemoteDir.
  If RemoteDir does not exist it is created.
  All files and subfolders of LocalDir will be uploaded.
  FTP parameters (username, password, server) must be already provided.
  The caller must connnect to the server before calling UploadFolderToFtp and disconnect after that }
procedure UploadFolderToFtp(FTP: TIdFTP; LocalDir, RemoteDir, Filter: string);


   procedure GoToSubDir(SubDir: string);
   VAR
     details, nodetails: TStringList;
     k: Integer;
   begin
    details   := TStringList.Create;       //get folder contents from ftp. one with details, one without
    nodetails := TStringList.Create;
    TRY
     FTP.List(details, '', True);
     FTP.List(nodetails, '', False);

     //we only want to have directories in the list (without '.' and '..')
     for k := details.Count - 1 downto 0 do
      if details.Strings[k] <> '' then
        if (PosInsensitive('dir', details.Strings[k]) < 1)
        OR (nodetails.Strings[k] = '.')
        OR (nodetails.Strings[k] = '..') then
         begin
          details.Delete(k);
          nodetails.Delete(k);
         end;

     //if our directory does not exists on the server, create it
     if nodetails.IndexOf(SubDir) = -1
     then FTP.MakeDir(SubDir);
    FINALLY
     FreeAndNil(Details);
     FreeAndNil(nodetails);
    END;
    FTP.ChangeDir(SubDir);   //change into next directory on server
   end;


   procedure uploadDir(dir: string);
   VAR
      s: string;
      List: TStringList;
   begin
     List:= ListDirectoriesOf(dir, FALSE, FALSE);        //iterate through subdirectories
     for s in List DO
      begin
       GoToSubDir(s);
       uploadDir(dir + s + '\');                         //and also locally go into the next subfolder
       FTP.ChangeDirUp;                                  //we have to go one directory up after leaving the recursion
      end;
     FreeAndNil(List);

     List:= ListFilesOf(dir, Filter, FALSE, FALSE);       //iterate through files
     for s in List DO
      begin
        Assert(s > '', 'File name should not be empty!');
        FTP.Put(dir + s, s);                           //if it's only a file, upload it to the current directory;
      end;
     FreeAndNil(List);
   end;


VAR
   subdir, dir: string;
begin
 //does not matter if RemoteDir is like 'dir\dir' or 'dir/dir'
 dir := StringReplace(RemoteDir, '\', '/', [rfReplaceAll]);

 if dir <> '' then
  begin
   if dir[1] = '/' then Delete(dir, 1, 1);     //remove first '/' if there's one
   if dir[Length(dir)] <> '/'  then dir := dir + '/';       // add a '/' at the end

   //loop through our remote-directories
   WHILE Pos('/', dir) > 0 DO
    begin
     SubDir:= system.Copy(dir, 1, Pos('/', dir) - 1);

     //if our directory does not exists on the server, we create it
     GoToSubDir(SubDir);

     //remove first directory from path ('your/directory/subdir/' --> 'directory/subdir/')
     Delete(dir, 1, Pos('/', dir));
    end;
  end;

 dir := LocalDir;
 if dir[Length(dir)] <> '\'
 then dir := dir + '\';

 uploadDir(dir);   // begin the upload
end;



procedure TfrmFTP.Button1Click(Sender: TObject);
begin
 if Connect       {Set parameters for FTP and connect to server }
 then UploadFolderToFtp(ftp, 'c:\test\', RemoteDir, '*.*')
 else SwMessage('Cannot connect to '+ FTP.ServerHOST);
 Disconnect;
end;

--

ListDirectoriesOf & ListFilesOf is a function that returns the subdirectories/files of the specified folder (it returns non-full path)

Aircondition answered 10/2, 2017 at 17:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.