使用PowerShell从FTP下载最新文件

5
我正在编写一段PowerShell脚本,该脚本将从FTP站点提取文件。这些文件每小时上传到FTP站点,因此我需要下载最新的文件。目前的代码会下载所有今天的文件,而不是只下载一个文件。如何让它只下载最新的文件?
以下是我当前使用的代码:
$ftpPath = 'ftp://***.***.*.*'
$ftpUser = '******'
$ftpPass = '******'
$localPath = 'C:\Temp'
$Date = get-date -Format "ddMMyyyy"
$Files = 'File1', 'File2'

function Get-FtpDir ($url, $credentials)
{
  $request = [Net.FtpWebRequest]::Create($url)
  if ($credentials) { $request.Credentials = $credentials }
  $request.Method = [System.Net.WebRequestMethods+FTP]::ListDirectory
  (New-Object IO.StreamReader $request.GetResponse().GetResponseStream()) -split "`r`n" 

}

$webclient = New-Object System.Net.WebClient 
$webclient.Credentials = New-Object System.Net.NetworkCredential($ftpUser,$ftpPass)  
$webclient.BaseAddress = $ftpPath

Foreach ( $item in $Files )
{
    Get-FTPDir $ftpPath $webclient.Credentials |
      ? { $_ -Like $item+$Date+'*' } |
      % {

          $webClient.DownloadFile($_, (Join-Path $localPath $_)) 
      }
}
2个回答

8
使用 FtpWebRequest 并不容易。对于您的任务,您需要知道文件时间戳。
不幸的是,使用 FtpWebRequest/.NET framework/PowerShell 提供的功能检索时间戳并没有真正可靠和高效的方法,因为它们不支持 FTP MLSD 命令。 MLSD 命令以标准化的机器可读格式提供远程目录列表。该命令和格式由RFC 3659标准化。
您可以使用 .NET framework 支持的以下备选方案:
  • ListDirectoryDetails 方法(FTP LIST 命令)检索目录中所有文件的详细信息,然后处理 FTP 服务器特定格式的详细信息(*nix 格式类似于 *nix 命令 ls 是最常见的,缺点是格式可能会随时间而变化,因为对于新文件使用 "May 8 17:48" 格式,而对于旧文件使用 "Oct 18 2009" 格式)
  • GetDateTimestamp 方法(FTP MDTM 命令)逐个检索每个文件的时间戳。优点是响应由RFC 3659标准化为 YYYYMMDDHHMMSS[.sss]。缺点是您必须为每个文件发送单独的请求,这可能相当低效。
一些参考资料:
或者,使用支持 MLSD 命令和/或支持专有列表格式解析的第三方 FTP 库。
例如,WinSCP .NET assembly 支持两者。
示例代码:
# Load WinSCP .NET assembly
Add-Type -Path "WinSCPnet.dll"

# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions -Property @{
    Protocol = [WinSCP.Protocol]::Ftp
    HostName = "example.com"
    UserName = "user"
    Password = "mypassword"
}

$session = New-Object WinSCP.Session

# Connect
$session.Open($sessionOptions)

# Get list of files in the directory
$directoryInfo = $session.ListDirectory($remotePath)

# Select the most recent file
$latest =
    $directoryInfo.Files |
    Where-Object { -Not $_.IsDirectory } |
    Sort-Object LastWriteTime -Descending |
    Select-Object -First 1

# Any file at all?
if ($latest -eq $Null)
{
    Write-Host "No file found"
    exit 1
}

# Download the selected file
$sourcePath = [WinSCP.RemotePath]::EscapeFileMask($remotePath + $latest.Name)
$session.GetFiles($sourcePath, $localPath).Check()

完整代码请参见下载最新文件(PowerShell)

(我是WinSCP的作者)


2
WinSCP是一个非常好用且非常可靠的实用工具 :) - Jimbo
1
WinSCP非常出色。对于使用PowerShell进行FTP传输非常高效。 - sodawillow

0

我尝试了这个,但是出现了一个错误:

Error: Exception calling "ListDirectory" with "1" argument(s): "Error listing directory '/path/'.
Could not retrieve directory listing
Can't open data connection for transfer of "/path/"

我在互联网上阅读了很多关于这个问题的文章,但是没有找到一个看起来相对简单的解决方案,而且我也不是网络设置专家。所以我选择了另一种方法。在我们的情况下,我想要自动下载的文件的文件名中指定了日期:backup_2018_08_03_020003_1048387.bak

因此,我们可以在命令行ftp会话中使用mget *2018_08_03*来获取文件。

我们的备份程序每天早上01:00运行一次,因此我们每天都可以获取备份。

当然,如果最新的备份出现问题或备份文件命名格式发生变化,基于备份文件时间戳获取最新备份文件的脚本会更美观、更好。该脚本只是为了内部开发目的而编写的备份脚本,如果它出现问题也不是什么大问题。我稍后会研究一下,并检查是否可以制定更干净的解决方案。

我编写了一个批处理脚本,它只需使用普通的ftp命令提示符脚本即可请求今天的备份文件。

正确获取今天日期的格式非常重要。它必须正确匹配文件名中的日期格式。

如果您想使用脚本,应该将变量替换为自己的信息。您还应该具有运行脚本所在目录的写入权限。

这是我制作的脚本:

@Echo Off
Set _FTPServerName=xxx.xxx.xx.xxx
Set _UserName=Username
Set _Password=Password
Set _LocalFolder=C:\Temp
Set _RemoteFolder="/path/"
Set _Filename=*%date:~-4,4%_%date:~-7,2%_%date:~-10,2%*
Set _ScriptFile=ftptempscript
:: Create script
 >"%_ScriptFile%" Echo open %_FTPServerName%
>>"%_ScriptFile%" Echo %_UserName%
>>"%_ScriptFile%" Echo %_Password%
>>"%_ScriptFile%" Echo lcd %_LocalFolder%
>>"%_ScriptFile%" Echo cd %_RemoteFolder%
>>"%_ScriptFile%" Echo binary
>>"%_ScriptFile%" Echo mget -i %_Filename%
>>"%_ScriptFile%" Echo quit
:: Run script
ftp -s:"%_ScriptFile%"
del "%_ScriptFile%"

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接