21
$ftpServer = "ftp.example.com"
$username ="validUser"
$password ="myPassword"
$localToFTPPath = "C:\ToFTP"
$localFromFTPPath = "C:\FromFTP"
$remotePickupDir = "/Inbox"
$remoteDropDir = "/Outbox"
$SSLMode = [AlexPilotti.FTPS.Client.ESSLSupportMode]::ClearText
$ftp = new-object "AlexPilotti.FTPS.Client.FTPSClient"
$cred = New-Object System.Net.NetworkCredential($username,$password)
$ftp.Connect($ftpServer,$cred,$SSLMode) #Connect
$ftp.SetCurrentDirectory($remotePickupDir)
$ftp.GetFiles($localFromFTPPath, $false) #Get Files

这是我从 FTP 服务器导入文件的脚本。
但是我不确定remotePickupDir这个脚本是什么,这个脚本是否正确?

4

7 回答 7

35

问题中使用的 AlexFTPS 库似乎已死(自 2011 年以来未更新)。


没有外部库

您可以尝试在没有任何外部库的情况下实现它。但不幸的是,.NET Framework 和 PowerShell 都没有明确支持下载目录中的所有文件(只允许递归文件下载)。

您必须自己实现:

  • 列出远程目录
  • 迭代条目,下载文件(以及可选地递归到子目录 - 再次列出它们等)

棘手的部分是从子目录中识别文件。FtpWebRequest使用 .NET 框架(或WebClient)无法以可移植的方式做到这一点。不幸的是,.NET 框架不支持该MLSD命令,这是在 FTP 协议中检索具有文件属性的目录列表的唯一可移植方式。另请参阅检查 FTP 服务器上的对象是文件还是目录

您的选择是:

  • 如果您知道该目录不包含任何子目录,请使用该ListDirectory方法(NLSTFTP 命令)并将所有“名称”下载为文件。
  • 对文件名执行操作,对于文件肯定会失败,而对于目录会成功(反之亦然)。即您可以尝试下载“名称”。
  • 您可能很幸运,在您的特定情况下,您可以通过文件名来区分目录中的文件(即所有文件都有扩展名,而子目录没有)
  • 您使用长目录列表(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 (($name -ne ".") -and ($name -ne ".."))
            {
                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# 中的 C# 示例中翻译的,通过 FTP 下载所有文件和子目录


使用第三方库

如果您想避免解析服务器特定目录列表格式的麻烦,请使用支持MLSD命令和/或解析各种LIST列表格式的第 3 方库。理想情况下,支持从目录下载所有文件,甚至递归下载。

例如,使用WinSCP .NET 程序集,您可以通过一次调用下载整个目录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()
}    

MLSD如果服务器支持,WinSCP 在内部使用该命令。如果没有,它会使用该LIST命令并支持数十种不同的列表格式。

Session.GetFiles方法默认是递归的。

(我是WinSCP的作者)

于 2016-10-05T16:15:01.247 回答
12

这是从 FTP 站点下载所有文件(带有通配符或文件扩展名)到本地目录的完整工作代码。设置变量值。

    #FTP Server Information - SET VARIABLES
    $ftp = "ftp://XXX.com/" 
    $user = 'UserName' 
    $pass = 'Password'
    $folder = 'FTP_Folder'
    $target = "C:\Folder\Folder1\"

    #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() 
        while(-not $reader.EndOfStream) {
            $reader.ReadLine()
        }
        #$reader.ReadToEnd()
        $reader.Close()
        $response.Close()
    }

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

    $files = Get-FTPDir -url $folderPath -credentials $credentials

    $files 

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

        #PRINT FILE NAME AND COUNTER
        $counter++
        $counter
        $source
    }
于 2016-03-21T19:25:22.550 回答
10

远程选择目录路径应该是您尝试访问的 ftp 服务器上的确切路径。这是从服务器下载文件的脚本。您可以使用 SSLMode 添加或修改。

#ftp server 
$ftp = "ftp://example.com/" 
$user = "XX" 
$pass = "XXX"
$SetType = "bin"  
$remotePickupDir = Get-ChildItem 'c:\test' -recurse
$webclient = New-Object System.Net.WebClient 

$webclient.Credentials = New-Object System.Net.NetworkCredential($user,$pass)  
foreach($item in $remotePickupDir){ 
    $uri = New-Object System.Uri($ftp+$item.Name) 
    #$webclient.UploadFile($uri,$item.FullName)
    $webclient.DownloadFile($uri,$item.FullName)
}
于 2013-09-28T03:53:09.927 回答
1

remotePickupDir将是您要在 ftp 服务器上访问的文件夹。就“这个脚本是否正确”而言,它有效吗?如果它有效,那么它是正确的。如果它不起作用,请告诉我们您收到的错误消息或意外行为,我们将能够更好地为您提供帮助。

于 2013-09-27T21:41:23.583 回答
1

基于为什么 FtpWebRequest 从根目录下载文件?这会导致 553 错误吗?,我编写了一个 PowerShell 脚本,可以通过显式 FTP over TLS 从 FTP 服务器下载文件:

# Config
$Username = "USERNAME"
$Password = "PASSWORD"
$LocalFile = "C:\PATH_TO_DIR\FILNAME.EXT"
#e.g. "C:\temp\somefile.txt"
$RemoteFile = "ftp://PATH_TO_REMOTE_FILE"
#e.g. "ftp://ftp.server.com/home/some/path/somefile.txt"

try{ 
    # Create a FTPWebRequest
    $FTPRequest = [System.Net.FtpWebRequest]::Create($RemoteFile)
    $FTPRequest.Credentials = New-Object System.Net.NetworkCredential($Username,$Password)
    $FTPRequest.Method = [System.Net.WebRequestMethods+Ftp]::DownloadFile
    $FTPRequest.UseBinary = $true
    $FTPRequest.KeepAlive = $false
    $FTPRequest.EnableSsl  = $true
    # Send the ftp request
    $FTPResponse = $FTPRequest.GetResponse()
    # Get a download stream from the server response
    $ResponseStream = $FTPResponse.GetResponseStream()
    # Create the target file on the local system and the download buffer
    $LocalFileFile = New-Object IO.FileStream ($LocalFile,[IO.FileMode]::Create)
    [byte[]]$ReadBuffer = New-Object byte[] 1024
    # Loop through the download

    do {
        $ReadLength = $ResponseStream.Read($ReadBuffer,0,1024)
        $LocalFileFile.Write($ReadBuffer,0,$ReadLength)
    }
    while ($ReadLength -ne 0)
    # Close file
    $LocalFileFile.Close()
}catch [Exception]
{
    $Request = $_.Exception
    Write-host "Exception caught: $Request"
}
于 2020-09-23T11:34:59.933 回答
0

Invoke-WebRequest 可以下载 HTTP、HTTPS 和 FTP 链接。

$source = 'ftp://Blah.com/somefile.txt'
$target = 'C:\Users\someuser\Desktop\BlahFiles\somefile.txt'
$password = Microsoft.PowerShell.Security\ConvertTo-SecureString -String 'mypassword' -AsPlainText -Force
$credential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList myuserid, $password

# Download
Invoke-WebRequest -Uri $source -OutFile $target -Credential $credential -UseBasicParsing

由于 cmdlet 使用 IE 解析,您可能需要 -UseBasicParsing 开关。测试以确保。

于 2021-02-15T17:26:12.647 回答
0

为了通过 powerShell 从 FTP 检索文件/文件夹,我编写了一些函数,您甚至可以从 FTP 获取隐藏的东西。

获取未隐藏在特定文件夹中的所有文件的示例:

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

获取特定文件夹中的所有文件夹(也隐藏)的示例:

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

您可以从以下模块复制功能而无需安装第三个库: https ://github.com/AstralisSomnium/PowerShell-No-Library-Just-Functions/blob/master/FTPModule.ps1

于 2016-09-26T13:12:09.597 回答