La libreria AlexFTPS utilizzata in que Stion sembra morto (non è stato aggiornato dal 2011).
In alternativa, è possibile provare a implementarlo senza alcuna libreria esterna. Ma sfortunatamente, né .NET Framework né PowerShell hanno alcun supporto esplicito per scaricare tutti i file in una directory (lasciare solo download di file ricorsivi).
è necessario implementare che da soli:
- Lista directory remota
- Iterate le voci, il download di file (e facoltativamente recursing in sottodirectory - messa in vendita di nuovo, ecc)
La parte difficile è identificare i file dalle sottodirectory. Non c'è modo di farlo in modo portatile con .NET framework (FtpWebRequest
o WebClient
). Il framework .NET purtroppo non supporta il comando MLSD
, che è l'unico modo portatile per recuperare l'elenco di directory con attributi di file nel protocollo FTP. Vedi anche Checking if object on FTP server is file or directory.
Le opzioni disponibili sono:
- Se si sa che la directory non contiene le sottodirectory, utilizzare il metodo
ListDirectory
(NLST
comando FTP) e semplicemente scaricare tutti i "nomi" come file.
- Esegue un'operazione su un nome file che ha esito negativo per il file e riesce per le directory (o viceversa). Cioè puoi provare a scaricare il "nome".
- Si può essere fortunati e nel tuo caso specifico, si può dire di un file da una directory con un nome di file (ad esempio tutti i file hanno l'estensione, mentre sottodirectory NON)
- si utilizza un lungo elenco di directory (
LIST
comando = ListDirectoryDetails
metodo) e tenta di analizzare un elenco specifico del server. Molti server FTP utilizzano l'elenco * nix-style, in cui si identifica una directory tramite lo d
all'inizio della voce. Ma molti server usano un formato diverso. L'esempio seguente utilizza questo approccio (supponendo che il formato * nix)
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()
}
}
}
Utilizzare la funzione come:
$credentials = New-Object System.Net.NetworkCredential("user", "mypassword")
$url = "ftp://ftp.example.com/directory/to/download/"
DownloadFtpDirectory $url $credentials "C:\target\directory"
Il codice viene tradotto da mia C# esempio nel C# Download all files and subdirectories through FTP.
Se si vuole evitare problemi con l'analisi dei formati di elenco di directory specifiche del server, utilizzare una libreria di terze parti che supporta il comando MLSD
e/o l'analisi vari formati LIST
quotazione. E idealmente con un supporto per il download di tutti i file da una directory o anche download ricorsivi.
Ad esempio, con WinSCP .NET assembly è possibile scaricare tutta la directory con una singola chiamata a Session.GetFiles
:
# 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()
}
Internamente, WinSCP usa il comando MLSD
, se supportato dal server. In caso contrario, utilizza il comando LIST
e supporta dozzine di formati di elenchi diversi.
Il Session.GetFiles
method è ricorsivo per impostazione predefinita.
(Io sono l'autore di WinSCP)
Se $ remotepickupdir = get-childitem c: \ test -recurse, PowerShell cercherà di ottenere tutti gli elementi in quel percorso quando si imposta la variabile. Quindi non può essere recuperato oggetti remoti perché a questo punto non c'è nemmeno stato un login. Il tuo codice sembra sbagliato. – BSAFH
Questo non dovrebbe essere contrassegnato come accettato. Questo non risponde alla domanda! –