PowerShell FTP下载文件和子文件夹

enyaitl3  于 2023-03-30  发布在  Shell
关注(0)|答案(2)|浏览(297)

我喜欢编写一个PowerShell脚本来从我的FTP服务器下载所有文件子文件夹。我发现一个脚本可以从一个特定文件夹下载所有文件,但我也喜欢下载子文件夹及其文件。

#FTP Server Information - SET VARIABLES
$ftp = "ftp://ftp.abc.ch/" 
$user = 'abc' 
$pass = 'abc'
$folder = '/'
$target = "C:\LocalData\Powershell"

#SET CREDENTIALS
$credentials = new-object System.Net.NetworkCredential($user, $pass)

function Get-FtpDir ($url,$credentials) {
    $request = [Net.WebRequest]::Create($url)
    $request.Method = [System.Net.WebRequestMethods+FTP]::ListDirectory
    if ($credentials) { $request.Credentials = $credentials }
    $response = $request.GetResponse()
    $reader = New-Object IO.StreamReader $response.GetResponseStream() 
    $reader.ReadToEnd()
    $reader.Close()
    $response.Close()
}

#SET FOLDER PATH
$folderPath= $ftp + "/" + $folder + "/"

$Allfiles=Get-FTPDir -url $folderPath -credentials $credentials
$files = ($Allfiles -split "`r`n")

$webclient = New-Object System.Net.WebClient 
$webclient.Credentials = New-Object System.Net.NetworkCredential($user,$pass) 
$counter = 0
foreach ($file in ($files | where {$_ -like "*.*"})){
    $source=$folderPath + $file  
    $destination = Join-Path $target $file 
    $webclient.DownloadFile($source, $destination)

    #PRINT FILE NAME AND COUNTER
    $counter++
    $counter
    $source
}

谢谢你的帮助(:

wlzqhblo

wlzqhblo1#

.NET framework或PowerShell没有任何对递归文件操作(包括下载)的显式支持。您必须自己实现递归:

  • 列出远程目录
  • 迭代条目,下载文件并递归到子目录(再次列出它们等)

棘手的部分是从子目录中识别文件。没有办法用.NET框架(FtpWebRequestWebClient)以可移植的方式做到这一点。不幸的是.NET框架不支持MLSD命令,这是在FTP协议中检索具有文件属性的目录列表的唯一可移植方式。另请参阅Checking if object on FTP server is file or directory
您的选项包括:

  • 对一个文件名做一个操作,这个操作对文件来说肯定会失败,对目录来说肯定会成功(反之亦然)。也就是说,你可以尝试下载“名称”。如果成功,它是一个文件,如果失败,它是一个目录。
  • 你可能很幸运,在你的特定情况下,你可以通过文件名来区分一个文件和一个目录(即所有文件都有扩展名,而子目录没有)。
  • 您使用一个长目录列表(LIST command = ListDirectoryDetails方法)并尝试解析特定于服务器的列表。许多FTP服务器使用 * nix样式的列表,其中您通过条目开头的d来标识目录。但许多服务器使用不同的格式。下面的示例使用这种方法(假设为 *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()
        }
    }
}

使用如下函数:

$credentials = New-Object System.Net.NetworkCredential("user", "mypassword") 
$url = "ftp://ftp.example.com/directory/to/download/"
DownloadFtpDirectory $url $credentials "C:\target\directory"

代码是从我的C# Download all files and subdirectories through FTP中的C#示例转换而来的。

如果你想避免解析特定于服务器的目录列表格式的麻烦,请使用支持MLSD命令和/或解析各种LIST列表格式的第三方库;和递归下载。
例如,使用WinSCP .NET assembly,您可以通过对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()
}

在内部,WinSCP使用MLSD命令,如果服务器支持。如果不支持,它使用LIST命令,并支持数十种不同的列表格式。
默认情况下,Session.GetFiles method是递归的。

  • (我是WinSCP的作者)*
zbdgwd5y

zbdgwd5y2#

通过PowerShell从FTP检索文件/文件夹我写了一些函数,你甚至可以从FTP隐藏的东西.
获取特定文件夹中的所有文件和子文件夹(即使是隐藏文件)的示例:

Get-FtpChildItem -ftpFolderPath "ftp://myHost.com/root/leaf/" -userName "User" -password "pw" -Directory -File

您可以直接从以下模块复制函数,而无需安装任何第三个库:https://github.com/AstralisSomnium/PowerShell-No-Library-Just-Functions/blob/master/FTPModule.ps1

相关问题