PowerShell FTP 下载文件和子文件夹
PowerShell FTP download files and subfolders
我喜欢编写一个 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")
$files
$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 = $target + $file
$webclient.DownloadFile($source, $target+$file)
#PRINT FILE NAME AND COUNTER
$counter++
$counter
$source
}
感谢您的帮助(:
.NET 框架或 PowerShell 没有对递归文件操作(包括下载)的任何明确支持。你必须自己实现递归:
- 列出远程目录
- 迭代条目、下载文件并递归到子目录(再次列出它们等)
棘手的部分是从子目录中识别文件。使用 .NET 框架(FtpWebRequest
或 WebClient
)无法以可移植的方式做到这一点。不幸的是,.NET 框架不支持 MLSD
命令,这是在 FTP 协议中检索具有文件属性的目录列表的唯一可移植方式。另见 .
您的选择是:
- 对文件名进行操作,对文件肯定会失败,对目录会成功(反之亦然)。 IE。您可以尝试下载“名称”。如果成功,它是一个文件,如果失败,它是一个目录。
- 你可能很幸运,在你的具体情况下,你可以通过文件名从目录中区分文件(即你的所有文件都有扩展名,而子目录没有)
- 您使用长目录列表(
LIST
命令= 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# 示例翻译而来的
虽然Microsoft does not recommend FtpWebRequest
for a new development.
如果您想避免解析特定于服务器的目录列表格式的麻烦,请使用支持 MLSD
命令 and/or 解析各种 LIST
列表格式的第 3 方库;和递归下载。
例如 WinSCP .NET assembly you can download whole directory with a single call to 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的作者)
为了通过 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
我喜欢编写一个 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")
$files
$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 = $target + $file
$webclient.DownloadFile($source, $target+$file)
#PRINT FILE NAME AND COUNTER
$counter++
$counter
$source
}
感谢您的帮助(:
.NET 框架或 PowerShell 没有对递归文件操作(包括下载)的任何明确支持。你必须自己实现递归:
- 列出远程目录
- 迭代条目、下载文件并递归到子目录(再次列出它们等)
棘手的部分是从子目录中识别文件。使用 .NET 框架(FtpWebRequest
或 WebClient
)无法以可移植的方式做到这一点。不幸的是,.NET 框架不支持 MLSD
命令,这是在 FTP 协议中检索具有文件属性的目录列表的唯一可移植方式。另见
您的选择是:
- 对文件名进行操作,对文件肯定会失败,对目录会成功(反之亦然)。 IE。您可以尝试下载“名称”。如果成功,它是一个文件,如果失败,它是一个目录。
- 你可能很幸运,在你的具体情况下,你可以通过文件名从目录中区分文件(即你的所有文件都有扩展名,而子目录没有)
- 您使用长目录列表(
LIST
命令=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"
代码是从我在
虽然Microsoft does not recommend FtpWebRequest
for a new development.
如果您想避免解析特定于服务器的目录列表格式的麻烦,请使用支持 MLSD
命令 and/or 解析各种 LIST
列表格式的第 3 方库;和递归下载。
例如 WinSCP .NET assembly you can download whole directory with a single call to 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的作者)
为了通过 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