使用PowerShell使用FTP上传文件


74

我想使用PowerShell将带有FTP的文件传输到匿名FTP服务器。我不会使用任何额外的程序包。怎么样?

脚本没有挂起或崩溃的风险。


JAMS Job Scheduler提供了使安全文件传输变得容易的cmdlet。使用cmdlet可以轻松自动化传输并使用各种协议进行连接。(FTP,SFTP等...)
user695859

Answers:


84

我不确定您是否可以100%地证明脚本不会挂起或崩溃,因为您无法控制某些事情(如果服务器在上载过程中断电了怎么办?)-但这应该为您入门提供坚实的基础:

# create the FtpWebRequest and configure it
$ftp = [System.Net.FtpWebRequest]::Create("ftp://localhost/me.png")
$ftp = [System.Net.FtpWebRequest]$ftp
$ftp.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile
$ftp.Credentials = new-object System.Net.NetworkCredential("anonymous","anonymous@localhost")
$ftp.UseBinary = $true
$ftp.UsePassive = $true
# read in the file to upload as a byte array
$content = [System.IO.File]::ReadAllBytes("C:\me.png")
$ftp.ContentLength = $content.Length
# get the request stream, and write the bytes into it
$rs = $ftp.GetRequestStream()
$rs.Write($content, 0, $content.Length)
# be sure to clean up after ourselves
$rs.Close()
$rs.Dispose()

如何捕捉错误?如果我无法连接怎么办?无法发送文件?连接断开了吗?我想处理错误并通知用户。
magol

16
这些都是与PowerShell脚本相关的非常好的个人问题,它们不仅可以处理ftp事务,还可以应用于更多方案。我的建议:在此处浏览PowerShell标记并阅读有关错误处理的信息。该脚本中大多数可能出错的地方都会引发异常,只需将脚本包装在可以处理该异常的内容中即可。
Goyuix 2009年

2
大型zip文件不是一个好的解决方案。当我尝试“ $ content = gc -en字节C:\ mybigfile.zip”时,powershell处理了很长时间。@CyrilGupta提出的解决方案对我来说更好。
wallybh 2012年

1
可能应始终将文件分成多个块,以避免$ content超出您可以处理的时间。类似于文档中的async示例。
jl。

只是我的经验中的简短笔记-在我删除凭据行(使用匿名访问)之前,这对我不起作用-不知道为什么!
Dewi Rees

47

也有其他方法。我使用了以下脚本:

$File = "D:\Dev\somefilename.zip";
$ftp = "ftp://username:password@example.com/pub/incoming/somefilename.zip";

Write-Host -Object "ftp url: $ftp";

$webclient = New-Object -TypeName System.Net.WebClient;
$uri = New-Object -TypeName System.Uri -ArgumentList $ftp;

Write-Host -Object "Uploading $File...";

$webclient.UploadFile($uri, $File);

您可以使用以下命令针对Windows FTP命令行实用工具运行脚本

ftp -s:script.txt 

(查看本文

关于SO的以下问题也可以回答此问题:如何编写脚本FTP上传和下载?


似乎没有办法使用此处显示的第一个选项关闭PASSIVE模式。
2012年

2
如果您的密码包含URL中不允许的字符,则创建$uri引发错误。我更喜欢在客户端上设置凭据:$webclient.Credentials = New-Object System.Net.NetworkCredential($user,$pass)
germankiwi's

当使用box.com FTP服务(仅支持被动模式)时,被动问题实际上是一个优势。在URL中不允许使用的字符中:这应该很有帮助...内置实用程序可以对URL进行编码/解码,例如在Powershell ftp中使用被动模式将其上传到box.com
Justin

该解决方案甚至可以在macOS上与PowerShell Core 6.1配合使用
HairOfTheDog,

29

我不会声称这比投票最高的解决方案更优雅...但这以它自己的方式很酷(嗯,至少在我看来,大声笑):

$server = "ftp.lolcats.com"
$filelist = "file1.txt file2.txt"   

"open $server
user $user $password
binary  
cd $dir     
" +
($filelist.split(' ') | %{ "put ""$_""`n" }) | ftp -i -in

如您所见,它使用了dinky内置的Windows FTP客户端。也更短而直接。是的,我实际上已经使用了它并且可以使用!


2
而且,如果您曾经使用过其他类型的FTP,那么您只是在管道传输到其他程序。真好
quillbreaker 2012年

3
这有点棘手(如果您将用户用户 密码分成三行,则不起作用,与使用脚本文件不同),并且没有文档记录(这是ftp中的-in开关),但确实有效!
basos

很棒的建议。我的测试显示正确的FTP命令是ftp.exe -i -n -d-这些开关均已记录在案。操作系统版本中的功能可能已更改,但是我根本无法运行发布的版本。此处的关键开关是-n-禁用自动登录。否则该USER命令无效。如果凭据位于单独的行(即[USERNAME]⏎[PASS]⏎运行FTP命令时的典型情况),则此重定向的输入方法将失败。这里的输入必须USER [USERNAME] [PASS]在后一行OPEN [HOSTNAME],按照之前的评论。
LeeM

14

最简单的方法

使用PowerShell将二进制文件上传到FTP服务器的最简单的方法是使用WebClient.UploadFile

$client = New-Object System.Net.WebClient
$client.Credentials = New-Object System.Net.NetworkCredential("username", "password")
$client.UploadFile("ftp://ftp.example.com/remote/path/file.zip", "C:\local\path\file.zip")

高级选项

如果您需要更大的控制权WebClient(例如TLS / SSL加密等),则无法使用,请使用FtpWebRequest。简单的方法是使用将复制FileStream到FTP流Stream.CopyTo

$request = [Net.WebRequest]::Create("ftp://ftp.example.com/remote/path/file.zip")
$request.Credentials = New-Object System.Net.NetworkCredential("username", "password")
$request.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile 

$fileStream = [System.IO.File]::OpenRead("C:\local\path\file.zip")
$ftpStream = $request.GetRequestStream()

$fileStream.CopyTo($ftpStream)

$ftpStream.Dispose()
$fileStream.Dispose()

进度监控

如果需要监视上传进度,则必须自己逐块复制内容:

$request = [Net.WebRequest]::Create("ftp://ftp.example.com/remote/path/file.zip")
$request.Credentials = New-Object System.Net.NetworkCredential("username", "password")
$request.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile 

$fileStream = [System.IO.File]::OpenRead("C:\local\path\file.zip")
$ftpStream = $request.GetRequestStream()

$buffer = New-Object Byte[] 10240
while (($read = $fileStream.Read($buffer, 0, $buffer.Length)) -gt 0)
{
    $ftpStream.Write($buffer, 0, $read)
    $pct = ($fileStream.Position / $fileStream.Length)
    Write-Progress `
        -Activity "Uploading" -Status ("{0:P0} complete:" -f $pct) `
        -PercentComplete ($pct * 100)
}

$ftpStream.Dispose()
$fileStream.Dispose()

上载资料夹

如果要从文件夹上传所有文件,请参阅
PowerShell脚本以将整个文件夹上传到FTP


6

我最近为powershell编写了一些与FTP通讯的功能,请参阅https://github.com/AstralisSomnium/PowerShell-No-Library-Just-Functions/blob/master/FTPModule.ps1。下面的第二个功能,您可以将整个本地文件夹发送到FTP。在模块中甚至还具有用于递归删除/添加/读取文件夹和文件的功能。

#Add-FtpFile -ftpFilePath "ftp://myHost.com/folder/somewhere/uploaded.txt" -localFile "C:\temp\file.txt" -userName "User" -password "pw"
function Add-FtpFile($ftpFilePath, $localFile, $username, $password) {
    $ftprequest = New-FtpRequest -sourceUri $ftpFilePath -method ([System.Net.WebRequestMethods+Ftp]::UploadFile) -username $username -password $password
    Write-Host "$($ftpRequest.Method) for '$($ftpRequest.RequestUri)' complete'"
    $content = $content = [System.IO.File]::ReadAllBytes($localFile)
    $ftprequest.ContentLength = $content.Length
    $requestStream = $ftprequest.GetRequestStream()
    $requestStream.Write($content, 0, $content.Length)
    $requestStream.Close()
    $requestStream.Dispose()
}

#Add-FtpFolderWithFiles -sourceFolder "C:\temp\" -destinationFolder "ftp://myHost.com/folder/somewhere/" -userName "User" -password "pw"
function Add-FtpFolderWithFiles($sourceFolder, $destinationFolder, $userName, $password) {
    Add-FtpDirectory $destinationFolder $userName $password
    $files = Get-ChildItem $sourceFolder -File
    foreach($file in $files) {
        $uploadUrl ="$destinationFolder/$($file.Name)"
        Add-FtpFile -ftpFilePath $uploadUrl -localFile $file.FullName -username $userName -password $password
    }
}

#Add-FtpFolderWithFilesRecursive -sourceFolder "C:\temp\" -destinationFolder "ftp://myHost.com/folder/" -userName "User" -password "pw"
function Add-FtpFolderWithFilesRecursive($sourceFolder, $destinationFolder, $userName, $password) {
    Add-FtpFolderWithFiles -sourceFolder $sourceFolder -destinationFolder $destinationFolder -userName $userName -password $password
    $subDirectories = Get-ChildItem $sourceFolder -Directory
    $fromUri = new-object System.Uri($sourceFolder)
    foreach($subDirectory in $subDirectories) {
        $toUri  = new-object System.Uri($subDirectory.FullName)
        $relativeUrl = $fromUri.MakeRelativeUri($toUri)
        $relativePath = [System.Uri]::UnescapeDataString($relativeUrl.ToString())
        $lastFolder = $relativePath.Substring($relativePath.LastIndexOf("/")+1)
        Add-FtpFolderWithFilesRecursive -sourceFolder $subDirectory.FullName -destinationFolder "$destinationFolder/$lastFolder" -userName $userName -password $password
    }
}

ReadAllBytes读取整个文件到内存。这不适用于大文件。即使对于中型文件,它的效率也很低。
Martin Prikryl

4

这是我的超酷版本,因为它有一个进步吧:-)

我知道这是一个完全没用的功能,但是看起来仍然很酷\ m / \ m /

$webclient = New-Object System.Net.WebClient
Register-ObjectEvent -InputObject $webclient -EventName "UploadProgressChanged" -Action { Write-Progress -Activity "Upload progress..." -Status "Uploading" -PercentComplete $EventArgs.ProgressPercentage } > $null

$File = "filename.zip"
$ftp = "ftp://user:password@server/filename.zip"
$uri = New-Object System.Uri($ftp)
try{
    $webclient.UploadFileAsync($uri, $File)
}
catch  [Net.WebException]
{
    Write-Host $_.Exception.ToString() -foregroundcolor red
}
while ($webclient.IsBusy) { continue }

PS。当我想知道“它停止工作了还是仅仅是我的慢速ASDL连接?”时,它有很大帮助。


很简约。在macOS上使用PowerShell Core 6.1.0时,显示了进度条,并且确实上传了文件,但进度条从未更新。(我使用500MB文件进行了测试,以确保它有足够的时间进行更新)
HairOfTheDog

2

您可以像这样简单地通过PowerShell处理文件上传。完整项目可在Github上找到,网址为https://github.com/edouardkombo/PowerShellFtp

#Directory where to find pictures to upload
$Dir= 'c:\fff\medias\'

#Directory where to save uploaded pictures
$saveDir = 'c:\fff\save\'

#ftp server params
$ftp = 'ftp://10.0.1.11:21/'
$user = 'user'
$pass = 'pass'

#Connect to ftp webclient
$webclient = New-Object System.Net.WebClient 
$webclient.Credentials = New-Object System.Net.NetworkCredential($user,$pass)  

#Initialize var for infinite loop
$i=0

#Infinite loop
while($i -eq 0){ 

    #Pause 1 seconde before continue
    Start-Sleep -sec 1

    #Search for pictures in directory
    foreach($item in (dir $Dir "*.jpg"))
    {
        #Set default network status to 1
        $onNetwork = "1"

        #Get picture creation dateTime...
        $pictureDateTime = (Get-ChildItem $item.fullName).CreationTime

        #Convert dateTime to timeStamp
        $pictureTimeStamp = (Get-Date $pictureDateTime).ToFileTime()

        #Get actual timeStamp
        $timeStamp = (Get-Date).ToFileTime() 

        #Get picture lifeTime
        $pictureLifeTime = $timeStamp - $pictureTimeStamp

        #We only treat pictures that are fully written on the disk
        #So, we put a 2 second delay to ensure even big pictures have been fully wirtten   in the disk
        if($pictureLifeTime -gt "2") {    

            #If upload fails, we set network status at 0
            try{

                $uri = New-Object System.Uri($ftp+$item.Name)

                $webclient.UploadFile($uri, $item.FullName)

            } catch [Exception] {

                $onNetwork = "0"
                write-host $_.Exception.Message;
            }

            #If upload succeeded, we do further actions
            if($onNetwork -eq "1"){
                "Copying $item..."
                Copy-Item -path $item.fullName -destination $saveDir$item 

                "Deleting $item..."
                Remove-Item $item.fullName
            }


        }  
    }
}   

2

Goyuix的解决方案效果很好,但是如显示的那样,它给了我这个错误:“使用HTTP代理时不支持所请求的FTP命令。”

$ftp.UsePassive = $true为我解决问题后添加此行:

$ftp.Proxy = $null;

1

您可以使用此功能:

function SendByFTP {
    param (
        $userFTP = "anonymous",
        $passFTP = "anonymous",
        [Parameter(Mandatory=$True)]$serverFTP,
        [Parameter(Mandatory=$True)]$localFile,
        [Parameter(Mandatory=$True)]$remotePath
    )
    if(Test-Path $localFile){
        $remoteFile = $localFile.Split("\")[-1]
        $remotePath = Join-Path -Path $remotePath -ChildPath $remoteFile
        $ftpAddr = "ftp://${userFTP}:${passFTP}@${serverFTP}/$remotePath"
        $browser = New-Object System.Net.WebClient
        $url = New-Object System.Uri($ftpAddr)
        $browser.UploadFile($url, $localFile)    
    }
    else{
        Return "Unable to find $localFile"
    }
}

该功能通过FTP发送指定的文件。您必须使用以下参数调用该函数:

  • userFTP =默认为“匿名”或您的用户名
  • passFTP =默认为“匿名”或您的密码
  • serverFTP = FTP服务器的IP地址
  • localFile =要发送的文件
  • remotePath = FTP服务器上的路径

例如 :

SendByFTP -userFTP "USERNAME" -passFTP "PASSWORD" -serverFTP "MYSERVER" -localFile "toto.zip" -remotePath "path/on/the/FTP/"

请详细说明您的代码的作用。仅代码答案在堆栈溢出中被认为是劣质的。
quinz

您不能Join-Path以这种方式在URL上使用。Join-Path使用反斜杠在默认情况下,当URL使用斜杠+您还需要URL编码userFTPpassFTP
马丁·普里克里
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.