Das Framework .NET oder Powershell keine explizite Unterstützung für rekursive Dateioperationen (einschließlich Downloads haben) Sie haben die Rekursion selbst zu implementieren:
- Liste der Remote-Verzeichnis
- Iterate die Einträge, Dateien und rekursiv in Unterverzeichnissen (Auflistung sie wieder, etc.)
Download
Tricky Teil ist die Identifizierung von Dateien aus Unterverzeichnissen. Es gibt keine Möglichkeit, dies auf portable Weise mit dem .NET-Framework zu tun (FtpWebRequest
oder WebClient
). Das .NET-Framework unterstützt leider nicht den Befehl MLSD
, der die einzige Möglichkeit ist, Verzeichnislisten mit Dateiattributen im FTP-Protokoll abzurufen. Siehe auch Checking if object on FTP server is file or directory.
Optionen sind verfügbar:
- Sie eine Operation auf einen Dateinamen, die Datei scheitern und erfolgreich für Verzeichnisse (oder umgekehrt) ist sicher. I.e. Sie können versuchen, den "Namen" herunterzuladen. Wenn das gelingt, ist es eine Datei, wenn das fehlschlägt, ist es ein Verzeichnis.
- können Sie Glück haben und in Ihrem speziellen Fall, können Sie eine Datei aus einem Verzeichnis durch einen Dateinamen (dh alle Dateien haben eine Erweiterung, während Unterverzeichnisse nicht) sagen
- Sie verwenden eine lange Verzeichnisliste (
LIST
command = ListDirectoryDetails
Methode) und versuchen, eine serverspezifische Auflistung zu analysieren. Viele FTP-Server verwenden eine * nix-style-Auflistung, in der Sie ein Verzeichnis durch die d
am Anfang des Eintrags identifizieren. Viele Server verwenden jedoch ein anderes Format. Das folgende Beispiel verwendet diesen Ansatz (vorausgesetzt, die * nix-Format)
function DownloadFtpDirectory($url, $credentials, $localPath)
{
$listRequest = [Net.WebRequest]::Create($url)
$listRequest.Method = [System.Net.WebRequestMethods+FTP]::ListDirectoryDetails
$listRequest.Credentials = $credentials
$lines = New-Object System.Collections.ArrayList
$listResponse = $listRequest.GetResponse()
$listStream = $listResponse.GetResponseStream()
$listReader = New-Object System.IO.StreamReader($listStream)
while (!$listReader.EndOfStream)
{
$line = $listReader.ReadLine()
$lines.Add($line) | Out-Null
}
$listReader.Dispose()
$listStream.Dispose()
$listResponse.Dispose()
foreach ($line in $lines)
{
$tokens = $line.Split(" ", 9, [StringSplitOptions]::RemoveEmptyEntries)
$name = $tokens[8]
$permissions = $tokens[0]
$localFilePath = Join-Path $localPath $name
$fileUrl = ($url + $name)
if ($permissions[0] -eq 'd')
{
if (!(Test-Path $localFilePath -PathType container))
{
Write-Host "Creating directory $localFilePath"
New-Item $localFilePath -Type directory | Out-Null
}
DownloadFtpDirectory ($fileUrl + "/") $credentials $localFilePath
}
else
{
Write-Host "Downloading $fileUrl to $localFilePath"
$downloadRequest = [Net.WebRequest]::Create($fileUrl)
$downloadRequest.Method = [System.Net.WebRequestMethods+FTP]::DownloadFile
$downloadRequest.Credentials = $credentials
$downloadResponse = $downloadRequest.GetResponse()
$sourceStream = $downloadResponse.GetResponseStream()
$targetStream = [System.IO.File]::Create($localFilePath)
$buffer = New-Object byte[] 10240
while (($read = $sourceStream.Read($buffer, 0, $buffer.Length)) -gt 0)
{
$targetStream.Write($buffer, 0, $read);
}
$targetStream.Dispose()
$sourceStream.Dispose()
$downloadResponse.Dispose()
}
}
}
Verwenden Sie die Funktion wie:
$credentials = New-Object System.Net.NetworkCredential("user", "mypassword")
$url = "ftp://ftp.example.com/directory/to/download/"
DownloadFtpDirectory $url $credentials "C:\target\directory"
Der Code von meinem C# Beispiel in C# Download all files and subdirectories through FTP übersetzt.
Wenn Sie Probleme vermeiden wollen, mit dem serverspezifischen Verzeichnis Parsen Formaten Auflistung, verwenden Sie eine 3rd-Party-Bibliothek, die den MLSD
Befehl und/oder Analysieren von verschiedenen LIST
Auflistung Formate unterstützt; und rekursive Downloads.
Zum Beispiel mit WinSCP .NET assembly können Sie ganzes Verzeichnis mit einem einzigen Aufruf Session.GetFiles
herunterladen:
# Load WinSCP .NET assembly
Add-Type -Path "WinSCPnet.dll"
# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property @{
Protocol = [WinSCP.Protocol]::Ftp
HostName = "ftp.example.com"
UserName = "user"
Password = "mypassword"
}
$session = New-Object WinSCP.Session
try
{
# Connect
$session.Open($sessionOptions)
# Download files
$session.GetFiles("/directory/to/download/*", "C:\target\directory\*").Check()
}
finally
{
# Disconnect, clean up
$session.Dispose()
}
Intern WinSCP den MLSD
Befehl verwendet, wenn vom Server unterstützt.Ist dies nicht der Fall, wird der Befehl LIST
verwendet und Dutzende verschiedener Listenformate unterstützt.
Die Session.GetFiles
method ist standardmäßig rekursiv.
(ich bin der Autor von WinSCP)
Vielen Dank sehr viel. Funktioniert perfekt mit winscp! – Pascal