Reputation: 3033
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?
Upvotes: 5
Views: 14151
Reputation: 11
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;
Upvotes: 1
Reputation: 21194
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)
Upvotes: 0
Reputation: 597036
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:
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.
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.
Upvotes: 9