Что нового

Получение дерева файлов и каталогов с FTP-сервера

Redline

AutoIT Гуру
Сообщения
506
Репутация
375
Функция рекурсивно обходит указанную директорию на FTP, на выходе получается массив (описание массива см. в _FTP_FindFileFirst) с полными путями до файлов/папок.
Ключ $iFilesAndFolders указывает что выводить в массив: только файлы/файлы + папки.

Новый вариант с изменением текущей директории сервера
Код:
#include <FTPEx.au3>
#include <Array.au3>
$hFtp = _FTP_Open('ftp_session')
$hSession = _FTP_Connect($hFtp, 'ftp-ip', 'usr', 'pswd')
Global $aList[1][6]
$aList[0][0] = 'Filename'
$aList[0][1] = 'Filesize'
$aList[0][2] = 'FileAttr'
$aList[0][3] = 'DateModif'
$aList[0][4] = 'DateCreate'
$aList[0][5] = 'DateAccess'
_FTP_GetList('', $aList, 0)
_FTP_Close($hFtp)
_ArrayDisplay($aList)

Func _FTP_GetList($sPath, ByRef $aList, $iFilesAndFolders = 1)
    $sPath = StringRegExpReplace($sPath, '(^(\\|/)+|(^.+))', '\\$3')
    _FTP_DirSetCurrent($hSession, $sPath)
    $aFind = _FTP_ListToArrayEx($hSession)
    If $aFind <> 0 Then
        For $i = 1 To $aFind[0][0]
            $iCount = UBound($aList)
            If BitAND($aFind[$i][2], $FILE_ATTRIBUTE_DIRECTORY) Then
                If $iFilesAndFolders = 1 Then
                    ReDim $aList[UBound($aList) + 1][6]
                    $aList[$iCount][0] = $sPath & '\' & $aFind[$i][0]
                    $aList[$iCount][1] = $aFind[$i][1]
                    $aList[$iCount][2] = $aFind[$i][2]
                    $aList[$iCount][3] = $aFind[$i][3]
                    $aList[$iCount][4] = $aFind[$i][4]
                    $aList[$iCount][5] = $aFind[$i][5]
					ConsoleWrite($sPath & '\' & $aFind[$i][0] & @CRLF)
                EndIf
                _FTP_GetList($sPath & '\' & $aFind[$i][0], $aList, $iFilesAndFolders)
            Else
                ReDim $aList[UBound($aList) + 1][6]
                $aList[$iCount][0] = $sPath & '\' & $aFind[$i][0]
                $aList[$iCount][1] = $aFind[$i][1]
                $aList[$iCount][2] = $aFind[$i][2]
                $aList[$iCount][3] = $aFind[$i][3]
                $aList[$iCount][4] = $aFind[$i][4]
                $aList[$iCount][5] = $aFind[$i][5]
				ConsoleWrite($sPath & '\' & $aFind[$i][0] & @CRLF)
            EndIf
        Next
    EndIf
EndFunc   ;==>_FTP_GetList

Первоначальный вариант:
Код:
#include <FTPEx.au3>
#include <Array.au3>
$hFtp = _FTP_Open('session_name')
$hSession = _FTP_Connect($hFtp, 'ftp_name/ftp_ip', 'name', 'passwd')
Global $aList[1][12]
_FTP_GetTree('', $aList, 0)
_FTP_Close($hFtp)
_ArrayDisplay($aList)

Func _FTP_GetTree($sPath, ByRef $aList, $iFilesAndFolders = 1)
	Local $aFind, $hFind, $iCounter = 0, $iPos, $iPlace

	$aFind = _FTP_FindFileFirst($hSession, $sPath, $hFind)
	While Not @error
		$iPos = 0
		$iPlace = UBound($aList)
		If BitAND($aFind[1], $FILE_ATTRIBUTE_DIRECTORY) Then
			If $iFilesAndFolders Then
				ReDim $aList[UBound($aList) + 1][12]
				$aList[0][0] = $iPlace
				$aList[$iPlace][0] = $aFind[0]
				$aList[$iPlace][1] = $aFind[1]
				$aList[$iPlace][2] = $aFind[2]
				$aList[$iPlace][3] = $aFind[3]
				$aList[$iPlace][4] = $aFind[4]
				$aList[$iPlace][5] = $aFind[5]
				$aList[$iPlace][6] = $aFind[6]
				$aList[$iPlace][7] = $aFind[7]
				$aList[$iPlace][8] = $aFind[8]
				$aList[$iPlace][9] = $aFind[9]
				$aList[$iPlace][10] = $sPath & '\' & $aFind[10]
				$aList[$iPlace][11] = $aFind[11]
			EndIf
			_FTP_FindFileClose($hFind)
			_FTP_GetTree($sPath & '\' & $aFind[10], $aList, $iFilesAndFolders)
			$aFind = _FTP_FindFileFirst($hSession, $sPath, $hFind)
			$iPos = $iCounter
		Else
			ReDim $aList[UBound($aList) + 1][12]
			$aList[0][0] = $iPlace
			$aList[$iPlace][0] = $aFind[0]
			$aList[$iPlace][1] = $aFind[1]
			$aList[$iPlace][2] = $aFind[2]
			$aList[$iPlace][3] = $aFind[3]
			$aList[$iPlace][4] = $aFind[4]
			$aList[$iPlace][5] = $aFind[5]
			$aList[$iPlace][6] = $aFind[6]
			$aList[$iPlace][7] = $aFind[7]
			$aList[$iPlace][8] = $aFind[8]
			$aList[$iPlace][9] = $aFind[9]
			$aList[$iPlace][10] = $sPath & '\' & $aFind[10]
			$aList[$iPlace][11] = $aFind[11]
		EndIf
		For $i = 0 To $iPos
			$aFind = _FTP_FindFileNext($hFind)
		Next
		$iCounter += 1
	WEnd
	_FTP_FindFileClose($hFind)
EndFunc   ;==>_FTP_GetTree
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Хорошая идея, но есть одно но. Это не будет работать для вложенных папок. В отличии от поиска на диске, где можно обойтись обычной рекурсией, для FTP использовать более одного вызова _FTP_FindFileFirst() одновременно для одной сессии не получится. Таким образом нужно для каждой следующей вложенной папки создавать новое соединение - _FTP_Connect(). Но здесь все может упереться в количество одновременных соединений разрешенных сервером с одного IP. Как-то так...

Вот здесь я писал похожий пример, но только на закачку файлов.


Добавлено:
Сообщение автоматически объединено:

Попробуй например с ftp.mozilla.org. Логин и пароль оставь пустыми строками.
 
Автор
Redline

Redline

AutoIT Гуру
Сообщения
506
Репутация
375
Yashied
Ваш способ был взят за основу :smile: + работа внутри одной сесиии.
Не знаю как с ftp.mozilla.org (из-за прокси мне туда не попасть), но с сервера на IIS внутри локалки все прекрасно работает (см. скриншот)

Сначала пробовал обойти сервер через _FTP_Command:
Код:
#include <FTPEx.au3>
#include <Array.au3>
$hFtp = _FTP_Open('session_name')
$hSession = _FTP_Connect($hFtp, 'ftp', 'usr', 'pass')
_FTP_GetList('')
_FTP_Close($hFtp)

Func _FTP_GetList($sPath)
	MsgBox(0, '_FTP_GetList', ' -> [' & $sPath & ']')
	_FTP_Command($hSession, 'cd ' & $sPath & @CRLF)
	If @error Then ConsoleWrite('error' & @CRLF)
	$aList = _FTP_ListToArrayEx($hSession)
	For $i = 1 To $aList[0][0]
		If BitAND($aList[$i][2], $FILE_ATTRIBUTE_DIRECTORY) Then
			_FTP_GetList($sPath & '\' & $aList[$i][0])
		Else
			ConsoleWrite($sPath & '\' & $aList[$i][0] & @CRLF)
		EndIf
	Next
EndFunc   ;==>_FTP_GetList

Но текущая директория не меняется, а так можно было бы получать лист текущей директории дальше через _FTP_ListToArrayEx
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Твой пример у меня перечислял только файлы и названия папок и дальше первого уровня не входил. Я тестировал на общих и своих (локальных) серверах. FTP сервера от FileZilla до Rumpus (Mac). С раздельными сессиями все работает на ура.

А ты уверен, что с локальными серверами IIS соединение идет через внешнюю сеть?
 
Автор
Redline

Redline

AutoIT Гуру
Сообщения
506
Репутация
375
Yashied [?]
А ты уверен, что с локальными серверами IIS соединение идет через внешнюю сеть?
Под локальным сервером подразумеваю сервер внутри одной локальной сети, причем адреса (мой и сервера) лежат в одной подсети, (может в этом дело?), внешняя сеть тут нигде не задействована.
Проверил сервера в других подсетях - тоже выдает содержимое подпапок, заголовки их платформ посмотрел в Total Commander:
220-FileZilla Server version 0.9.34 beta
220 Microsoft FTP Service
 

Yashied

Модератор
Команда форума
Глобальный модератор
Сообщения
5,379
Репутация
2,724
Странно... Может от Windows зависит. Я проверял только на XP. Нужно, чтобы другие пользователи тоже проверили.
 

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
У меня FTP при запросе команды CD возвращает 500 Unknown command.
И возвращает только первую папку. Потом эту папку складывает до бесконечности, в результате получается строка -> [\Data\Data\Data\Data]
 
Автор
Redline

Redline

AutoIT Гуру
Сообщения
506
Репутация
375
inververs
Эта функция не рабочая и была, надо было проверить функцию из первого поста.
Но у меня получилось реанимировать вторую функцию :laugh: , команда не нужна не CD, а CWD (change work directory), даже не знаю откуда я взял вариант CD
Вот прошу протестировать, со всеми изменениями:
Код:
#include <FTPEx.au3>
#include <Array.au3>
$hFtp = _FTP_Open('session_name')
$hSession = _FTP_Connect($hFtp, 'ftp_addr', 'usr', 'pswd')
Global $aList[1][6]
$aList[0][0] = 'Filename'
$aList[0][1] = 'Filesize'
$aList[0][2] = 'FileAttr'
$aList[0][3] = 'DateModif'
$aList[0][4] = 'DateCreate'
$aList[0][5] = 'DateAccess'
_FTP_GetList('', $aList, 0)
_FTP_Close($hFtp)
_ArrayDisplay($aList)

Func _FTP_GetList($sPath, ByRef $aList, $iFilesAndFolders = 1)
	$sPath = StringRegExpReplace($sPath, '(^(\\|/)+|(^.+))', '\\$3')
	_FTP_Command($hSession, 'CWD ' & $sPath & @CRLF)
	$aFind = _FTP_ListToArrayEx($hSession)
	If $aFind <> 0 Then
		For $i = 1 To $aFind[0][0]
			$iCount = UBound($aList)
			If BitAND($aFind[$i][2], $FILE_ATTRIBUTE_DIRECTORY) Then
				If $iFilesAndFolders = 1 Then
					ReDim $aList[UBound($aList) + 1][6]
					$aList[$iCount][0] = $sPath & '\' & $aFind[$i][0]
					$aList[$iCount][1] = $aFind[$i][1]
					$aList[$iCount][2] = $aFind[$i][2]
					$aList[$iCount][3] = $aFind[$i][3]
					$aList[$iCount][4] = $aFind[$i][4]
					$aList[$iCount][5] = $aFind[$i][5]
				EndIf
				_FTP_GetList($sPath & '\' & $aFind[$i][0], $aList, $iFilesAndFolders)
			Else
				ReDim $aList[UBound($aList) + 1][6]
				$aList[$iCount][0] = $sPath & '\' & $aFind[$i][0]
				$aList[$iCount][1] = $aFind[$i][1]
				$aList[$iCount][2] = $aFind[$i][2]
				$aList[$iCount][3] = $aFind[$i][3]
				$aList[$iCount][4] = $aFind[$i][4]
				$aList[$iCount][5] = $aFind[$i][5]
			EndIf
		Next
	EndIf
EndFunc   ;==>_FTP_GetList

PS: Добавил регулярку для нормальной работы с путем поиска (добавляет "\", если его нет, или удаляет повторения "\" и "/" в начале строки)
 
Верх