Сценарий
Я пытаюсь воспроизвести стандартный способ заполнения Treeview каталогами/папками из структуры папок, начиная с корня, но используя IdFTP для получения структуры с удаленного сервера вместо моего локального жесткого диска. Я бы хотел, чтобы результат был похож на такие клиенты, как Filezilla.
Я использовал этот достаточно стандартный код из Swiss Delphi Center (который работает для отображения структуры моего жесткого диска), а затем изменил его, чтобы использовать IdFTP.ChangeDir(Directory) и IdFTP.List вместо FindFirst() и FindNext().
Проблема
Кажется, я запутался, так как рекурсия неправильно «раскручивается», так что, как только она проходит вниз по каталогам /cpanel/cash/config на удаленном сервере, она не возвращается и не проходит по всем другим висящим каталогам. root, но выходит из процедуры, не отображая ничего другого. Также кажется, что не отображаются все папки верхнего уровня, но это может быть просто из-за порядка, в котором IdFTP.List возвращает их в
Может ли кто-нибудь сказать мне, что я сделал неправильно здесь?
Если вы также можете сказать мне, как мне показать корень (/), это было бы очень полезно
(Я закомментировал отображение не каталогов, так как на этом этапе мне нужны только папки)
Что я ожидал увидеть Скопировано с Filezilla
Что я увидел Использование Ttreeview в Delphi
Мой код
procedure TForm2.Button1Click(Sender: TObject);
var StartingDir : string;
begin
TreeView1.Items.BeginUpdate;
try
StartingDir := '/';
Screen.Cursor := crHourGlass;
TreeView1.Items.Clear;
FTPconnect; //procedure to connect to remote server
GetDirectories(TreeView1, StartingDir, nil, True);
FTPDisconnect; //procedure to disconnect from remote server
finally
TreeView1.Items.EndUpdate;
Screen.Cursor := crDefault;
end;
end;
procedure TForm2.GetDirectories(Tree: TTreeView; Directory: string; Item: TTreeNode; IncludeFiles: Boolean);
var
ItemTemp: TTreeNode;
DirItemType : TIdDirItemType ;
Filename , NewStartingDirectory: string;
i : Integer;
begin
Tree.Items.BeginUpdate;
IdFTP.ChangeDir(Directory);
IdFTP.List; //get directory of remote folder
i:=0;
repeat
DirItemType := IdFTP.DirectoryListing[I].ItemType;
Filename := IdFTP.DirectoryListing[I].FileName;
If (DirItemType = ditDirectory) and (Filename <> '.') and (Filename <> '..')then
begin
if DirItemType = ditDirectory then
Item := Tree.Items.AddChild(Item, Filename);
ItemTemp := Item.Parent;
if Directory = '/' then
NewStartingDirectory := Directory + Filename
else
NewStartingDirectory := Directory + '/' +Filename;
GetDirectories(Tree, NewStartingDirectory, Item, IncludeFiles);
Item := ItemTemp;
end
else
if IncludeFiles then
begin //this bit commented out as we only want to see directories
// if (Filename <> '.') and (Filename <> '..') then
// Tree.Items.AddChild(Item, Filename);
end;
inc(i);
until i = IdFTP.DirectoryListing.Count;
Tree.Items.EndUpdate;
end;
Код Swiss Delhpi Centre (для сравнения)
procedure TForm1.Button1Click(Sender: TObject);
var
Node: TTreeNode;
Path: string;
Dir: string;
begin
Dir := 'c:\temp';
Screen.Cursor := crHourGlass;
TreeView1.Items.BeginUpdate;
try
TreeView1.Items.Clear;
GetDirectories(TreeView1, Dir, nil, True);
finally
Screen.Cursor := crDefault;
TreeView1.Items.EndUpdate;
end;
end;
procedure TForm1.GetDirectories(Tree: TTreeView; Directory: string; Item: TTreeNode; IncludeFiles: Boolean);
var
SearchRec: TSearchRec;
ItemTemp: TTreeNode;
begin
Tree.Items.BeginUpdate;
if Directory[Length(Directory)] <> '\' then Directory := Directory + '\';
if FindFirst(Directory + '*.*', faDirectory, SearchRec) = 0 then
begin
repeat
if (SearchRec.Attr and faDirectory = faDirectory) and (SearchRec.Name[1] <> '.') then
begin
if (SearchRec.Attr and faDirectory > 0) then
Item := Tree.Items.AddChild(Item, SearchRec.Name);
ItemTemp := Item.Parent;
GetDirectories(Tree, Directory + SearchRec.Name, Item, IncludeFiles);
Item := ItemTemp;
end
else if IncludeFiles then
if SearchRec.Name[1] <> '.' then
Tree.Items.AddChild(Item, SearchRec.Name);
until FindNext(SearchRec) <> 0;
FindClose(SearchRec);
end;
Tree.Items.EndUpdate;
end;
Я посмотрел ТАК здесь - слишком сложный и неправильный язык и здесь - похоже на швейцарский Delphi Center и здесь - неправильный язык и не уверен, что он делает.
если лучше использовать TlistView, не могли бы вы показать мне эквивалентный код для его использования?
@AmigoJack Спасибо. Я добавил переменную TheDirectoryListing : TIdFTPListItems;
, добавил directoryListing := IdFTP.DirectoryListing;
сразу после IdFTP.List
и изменил ссылки на IdFTP.DirectoryListing
на TheDirectoryListing;
, но это не имело значения, я получил тот же результат. Как я и ожидал, я все еще звоню IdFTP.list
на каждой итерации. Я, должно быть, неправильно понял, пожалуйста, уточните.
Вы просто скопировали ссылку, а не содержание. В качестве альтернативы можно выполнить итерацию IdFTP.DirectoryListing
без рекурсии и сохранить только папки в TStringList
— после этого вы можете выполнить рекурсию по своему собственному списку.
Это имеет смысл. Подскажите, как скопировать содержимое IdFTP.DirectoryListing?
Точно так же, как вы это делаете сейчас, но вместо того, чтобы добавлять новый узел дерева и повторно вызывать свой собственный метод, вы добавляете папку в свой список как текст.
Извините, я имел в виду, как мне правильно сделать directoryListing := IdFTP.DirectoryListing;
, но скопировать содержимое IdFTP.DirectoryListing;
вместо ссылки на него, как вы предложили (я хотел бы сначала попробовать это, прежде чем переписывать все это, чтобы покончить с рекурсией, сохраняя папки и т. д. )
Точно так же, как вы копируете содержимое объекта (например, TStringList
) — либо ищите его, либо выделяйте его в отдельный вопрос.
Непроверенный:
TIdFTP
я сделал параметром, так как TTreeView
тоже был один и надо делать последовательно, а не архаично.for
вместо repeat until
.IncludeFiles
, когда оно все равно не использовалось.FTP
не имеет значения, и вы не перепутаете списки на разных уровнях.procedure TForm2.GetFolders
( Ftp: TIdFTP // The source, from which we read the content
; Tree: TTreeView // The destination, which we want to fill
; ParentNode: TTreeNode // Node under which all new child nodes should be created
; Path: String // Starting directory
);
var
NewNode: TTreeNode; // New child in the tree
Filename: String; // Check against unwanted folder entries
i: Integer; // Looping over both lists
sl: TStringList; // Collect folders only
begin
FTP.ChangeDir( Path );
FTP.List; // Entire remote listing
sl:= TStringList.Create; // Collect all entries we're interested in
try
for i:= 0 to FTP.DirectoryListing.Count- 1 do begin // For each entry
Filename:= FTP.DirectoryListing[i].FileName;
if (FTP.DirectoryListing[i].ItemType= ditDirectory) // Only folders
and (Filename<> '.')
and (Filename<> '..') then begin
sl.Add( Filename ); // Only the name, not the full path
end;
end;
// Do this only once
if Path<> '/' then Path:= '/'+ Path+ '/';
for i:= 0 to sl.Count- 1 do begin // All collected folders
NewNode:= Tree.Items.AddChild( ParentNode, sl[i] ); // Populate tree
GetFolders( Ftp, Tree, NewNode, Path+ sl[i] ); // Recursion of folder name + current path
end;
finally
sl.Free;
end;
end;
Не тестировалось, но должно компилироваться.
Спасибо, что взяли на себя труд закодировать это. Понятно, что мне пришлось изменить прототип процедуры с Procedure GetFolders;
на Procedure GetFolders ( FTP : TIdFTP; Tree : TTreeView; ParentNode :TTreeNode; Path: String );
, но тогда этот альтернативный метод дал желаемый результат. Я принял ваш ответ, поскольку он отвечает на вопрос в заголовке, вводя дополнительный шаг, и я вижу логику этого. Мне все же было бы интересно посмотреть, можно ли это сделать, используя достаточно стандартный алгоритм в моем посте, но вместо этого используя методы FTP, если findfirst/findnext. - Может и не может.
Я нигде не определял procedure GetFolders;
без параметров (но да: вы также должны объявить его в своем классе Form). Нет, нет "первого/следующего", потому что вы вытягиваете весь список сразу - так работает FTP в отличие от файловых систем. Это также не решит ваше непонимание того, что вы глобально работаете с одним и тем же экземпляром списка вместо того, чтобы иметь отдельные экземпляры списка для каждой рекурсии. Вся ваша проблема заключается в том, что вы не можете отличить экземпляры от ссылок. Задайте/ищите вопрос об этой концепции, чтобы получить ответ.
Я имел в виду, что ваш код имеет «процедуру TForm2.GetFolders;» вверху, без параметров, но рекурсивный вызов GetFolders( Ftp, Tree, NewNode, Path+ sl[i] );
с четырьмя параметрами.
Нет, это не так. Присмотритесь: точки с запятой нет — определение продолжается в следующих строках. Вы также ставите then begin
в несколько строк, а я нет. Нет необходимости запихивать все в одну строку. В качестве альтернативы объясните мне, почему строки 2-6 в моем коде не вызывают вопросов у вас.
Вы должны сохранить содержимое
IdFTP.DirectoryListing
в своей собственной переменной, иначе следующий вызовIdFTP.List()
изменит его для каждой итерации рекурсии, независимо от того, где вы сейчас находитесь. Если у/.cpanel/caches/config
нет или есть одна запись, тогдаIdFTP.DirectoryListing.Count
будет0
или1
для всего и выйдите из цикла именно с этой логикой.