【问题标题】:How to script FTP upload and download如何编写 FTP 上传和下载脚本
【发布时间】:2010-10-30 11:35:59
【问题描述】:

我正在尝试制作一个批处理文件以将文件上传到 FTP 服务器。 如果我手动输入它可以正常工作,但是当我运行批处理文件时,它会在连接后停止......它说:

connected to domain.com.

220 microsoft ftp server

User(domain.com:(none)):

然后没有别的了。这是怎么回事?

下面是我的批处理文件:

ftp www.domainhere.com

user useridhere

passwordhere

put test.txt

bye

pause

【问题讨论】:

标签: command-line powershell ftp


【解决方案1】:

我遇到了类似的问题 - 就像原始海报一样,我想自动上传文件,但我不知道怎么做。因为这是在我家商店的注册终端上,所以我不想安装 PowerShell(尽管这看起来很简单),我只想要一个简单的 .bat 文件来执行此操作。

这几乎就是grawity和另一位用户所说的;我是新手,所以这里有一个更详细的示例和解释(还要感谢 How to Automate FTP Uploads from the Windows Command Line,他解释了如何只用一个 .bat 文件来完成它)。

基本上您需要两个文件 - 一个 .bat 和一个 .txt。 .bat 告诉 ftp.exe 使用什么开关。 .txt 提供了 ftp.exe 的命令列表。在文本文件中输入:

username
password
cd whereverYouWantToPutTheFile
lcd whereverTheFileComesFrom
put C:\InventoryExport\inventory.test (or your file path)
bye

将其保存在您想要的任何位置。在BAT文件中放:

ftp.exe -s:C:\Windows\System32\test.txt destinationIP
pause

显然将-s: 之后的路径更改为您的文本文件所在的位置。当你实际运行它时去掉暂停——它只是为了你可以看到任何错误。当然,您可以在 .txt 文件中使用“get”或任何其他 ftp 命令来执行您需要执行的任何操作。

我不确定您是否需要文本文件中的lcd 命令。就像我说的,我不熟悉使用命令行来处理这类事情,但这对我有用。

【讨论】:

    【解决方案2】:

    您可以使用-s:filename 选项编写ftp 命令的脚本。语法只是传递给ftp shell 的命令列表,每个命令都以换行符结束。 This page 很好地引用了可以使用 ftp 执行的命令。

    上传/下载整个目录结构

    当您需要将整个目录树复制到 FTP 站点或从 FTP 站点复制时,使用普通的ftp 效果不佳。所以你可以使用类似的东西来处理这些情况。

    这些脚本与 Windows ftp 命令一起使用,并允许通过单个命令上传和下载整个目录。这使得它在不同系统上使用时非常独立。

    基本上,它们映射出要上传/下载的目录结构,将相应的ftp 命令转储到文件中,然后在映射完成后执行这些命令。

    ftpupload.bat

    @echo off
    
    SET FTPADDRESS=%1
    SET FTPUSERNAME=%2
    SET FTPPASSWORD=%3
    SET LOCALDIR=%~f4
    SET REMOTEDIR=%5
    
    if "%FTPADDRESS%" == "" goto FTP_UPLOAD_USAGE
    if "%FTPUSERNAME%" == "" goto FTP_UPLOAD_USAGE
    if "%FTPPASSWORD%" == "" goto FTP_UPLOAD_USAGE
    if "%LOCALDIR%" == "" goto FTP_UPLOAD_USAGE
    if "%REMOTEDIR%" == "" goto FTP_UPLOAD_USAGE
    
    :TEMP_NAME
    set TMPFILE=%TMP%\%RANDOM%_ftpupload.tmp
    if exist "%TMPFILE%" goto TEMP_NAME
    
    SET INITIALDIR=%CD%
    
    echo user %FTPUSERNAME% %FTPPASSWORD% > %TMPFILE%
    echo bin >> %TMPFILE%
    echo lcd %LOCALDIR% >> %TMPFILE%
    
    cd %LOCALDIR%
    
    setlocal EnableDelayedExpansion
    echo mkdir !REMOTEDIR! >> !TMPFILE!
    echo cd %REMOTEDIR% >> !TMPFILE!
    echo mput * >> !TMPFILE!
    for /d /r %%d in (*) do (
        set CURRENT_DIRECTORY=%%d
        set RELATIVE_DIRECTORY=!CURRENT_DIRECTORY:%LOCALDIR%=!
        echo mkdir "!REMOTEDIR!/!RELATIVE_DIRECTORY:~1!" >> !TMPFILE!
        echo cd "!REMOTEDIR!/!RELATIVE_DIRECTORY:~1!" >> !TMPFILE!
        echo mput "!RELATIVE_DIRECTORY:~1!\*" >> !TMPFILE!
    )
    
    echo quit >> !TMPFILE!
    
    endlocal EnableDelayedExpansion
    
    ftp -n -i "-s:%TMPFILE%" %FTPADDRESS%
    
    del %TMPFILE%
    
    cd %INITIALDIR%
    
    goto FTP_UPLOAD_EXIT
    
    :FTP_UPLOAD_USAGE
    
    echo Usage:    ftpupload [address] [username] [password] [local directory] [remote directory]
    echo.
    
    :FTP_UPLOAD_EXIT
    
    set INITIALDIR=
    set FTPADDRESS=
    set FTPUSERNAME=
    set FTPPASSWORD=
    set LOCALDIR=
    set REMOTEDIR=
    set TMPFILE=
    set CURRENT_DIRECTORY=
    set RELATIVE_DIRECTORY=
    
    @echo on
    

    ftpget.bat

    @echo off
    
    SET FTPADDRESS=%1
    SET FTPUSERNAME=%2
    SET FTPPASSWORD=%3
    SET LOCALDIR=%~f4
    SET REMOTEDIR=%5
    SET REMOTEFILE=%6
    
    if "%FTPADDRESS%" == "" goto FTP_UPLOAD_USAGE
    if "%FTPUSERNAME%" == "" goto FTP_UPLOAD_USAGE
    if "%FTPPASSWORD%" == "" goto FTP_UPLOAD_USAGE
    if "%LOCALDIR%" == "" goto FTP_UPLOAD_USAGE
    if not defined REMOTEDIR goto FTP_UPLOAD_USAGE
    if not defined REMOTEFILE goto FTP_UPLOAD_USAGE
    
    :TEMP_NAME
    set TMPFILE=%TMP%\%RANDOM%_ftpupload.tmp
    if exist "%TMPFILE%" goto TEMP_NAME
    
    echo user %FTPUSERNAME% %FTPPASSWORD% > %TMPFILE%
    echo bin >> %TMPFILE%
    echo lcd %LOCALDIR% >> %TMPFILE%
    
    echo cd "%REMOTEDIR%" >> %TMPFILE%
    echo mget "%REMOTEFILE%" >> %TMPFILE%
    echo quit >> %TMPFILE%
    
    ftp -n -i "-s:%TMPFILE%" %FTPADDRESS%
    
    del %TMPFILE%
    
    goto FTP_UPLOAD_EXIT
    
    :FTP_UPLOAD_USAGE
    
    echo Usage:    ftpget [address] [username] [password] [local directory] [remote directory] [remote file pattern]
    echo.
    
    :FTP_UPLOAD_EXIT
    
    set FTPADDRESS=
    set FTPUSERNAME=
    set FTPPASSWORD=
    set LOCALDIR=
    set REMOTEFILE=
    set REMOTEDIR=
    set TMPFILE=
    set CURRENT_DIRECTORY=
    set RELATIVE_DIRECTORY=
    
    @echo on
    

    【讨论】:

      【解决方案3】:

      我遇到了同样的问题,并使用类似于Cheeso provided 的解决方案解决了它。

      “不起作用,说需要密码,尝试了几种不同的方法”

      是的,这是因为通过命令文件进行的 FTP 会话不需要用户名以字符串“user”开头。放下它,然后尝试一下。

      或者,您可能会看到这种情况,因为您的 FTP 命令文件未正确编码(这也让我很不爽)。这是关于在运行时生成 FTP 命令文件的糟糕部分。 PowerShell 的输出文件 cmdlet 没有 Windows FTP 将接受的编码选项(至少我找不到)。

      无论如何,做一个 WebClient.DownloadFile 是要走的路。

      【讨论】:

      • 您遇到了这个问题,因为 OUT-FILE 默认使用 Unicode 编码。这会将两个字节添加到文件的开头,这会导致两个字节被传递到 ftp 输入流时出现问题。通过使用 OUT-FILE -Encoding ASCII 来克服它...
      【解决方案4】:

      用你的命令创建一个命令文件。

      即文件commands.txt

      open www.domainhere.com
      user useridhere
      passwordhere
      put test.txt
      bye
      

      然后从命令行运行 FTP 客户端:

      ftp -s:commands.txt
      

      注意:这适用于 Windows FTP 客户端。

      【讨论】:

      • 不起作用,说密码是必需的,尝试了几种不同的方法
      • @payling 哦,来吧!您只需要跳过用户名前的user,它就像做梦一样。
      • 如果我有更多的赞成票 ;)。顺便说一句,在 Linux 上,您可以使用 .netrc 和管道命令到 ftp 执行此操作。
      【解决方案5】:

      希望按照原始发帖人想象的方式编写 FTP 会话脚本是一个合理的想法,而这正是 Expect 会提供帮助的事情。 Windows 上的批处理文件无法做到这一点。

      但是,与使用 cURL 或 Expect 相比,您可能会发现编写 FTP 与 PowerShell 交互的脚本更容易。这是一个不同的模型,因为您不是直接编写要发送到 FTP 服务器的文本的脚本。相反,您将使用 PowerShell 来操作为您生成 FTP 对话的对象。

      上传:

      $File = "D:\Dev\somefilename.zip"
      $ftp = "ftp://username:password@example.com/pub/incoming/somefilename.zip"
      
      "ftp url: $ftp"
      
      $webclient = New-Object System.Net.WebClient
      $uri = New-Object System.Uri($ftp)
      
      "Uploading $File..."
      
      $webclient.UploadFile($uri, $File)
      

      下载:

      $File = "c:\store\somefilename.zip"
      $ftp = "ftp://username:password@example.com/pub/outbound/somefilename.zip"
      
      "ftp url: $ftp"
      
      $webclient = New-Object System.Net.WebClient
      $uri = New-Object System.Uri($ftp)
      
      "Downloading $File..."
      
      $webclient.DownloadFile($uri, $File)
      

      您需要 PowerShell 来执行此操作。如果您不知道,PowerShell 是一个类似于 cmd.exe 的 shell,它运行您的 .bat 文件。但是 PowerShell 运行 .ps1 文件,并且功能强大得多。 PowerShell 是 Windows 的免费附加组件,将内置到未来版本的 Windows 中。 Get it here.

      来源:http://poshcode.org/1134

      【讨论】:

      • Hrrm,看看我如何无法让批处理按我想要的方式运行,这看起来是个好主意......我刚刚完成安装它.. 不完全确定我在做什么,但我'会给它几分钟的时间。我可以将 Windows 调度程序与它结合起来以在设定的时间间隔内运行我的脚本吗?
      • 好的,我已经可以正确上传和下载了。我将如何实现流程自动化?
      • 你做得很好。如果您在 powershell 中进行 FTP,那么它自动化的。你想要的是安排它。这意味着 schtasks.exe。 support.microsoft.com/kb/814596
      • 按计划运行powershell的方法是给调度器指定这个命令行:c:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe
      • 你会如何修改这个来上传多个不同扩展名的文件?
      【解决方案6】:

      此脚本生成命令文件,然后将命令文件通过管道传输到 ftp 程序,并在此过程中创建日志。最后打印原始bat文件、命令文件和本次会话的日志。

      @echo on
      @echo off > %0.ftp
      ::== GETmy!dir.bat
      >> %0.ftp echo a00002t
      >> %0.ftp echo iasdad$2
      >> %0.ftp echo help
      >> %0.ftp echo prompt
      >> %0.ftp echo ascii
      >> %0.ftp echo !dir REPORT.CP1C.ROLLEDUP.TXT
      >> %0.ftp echo get REPORT.CP1C.ROLLEDUP.TXT
      >> %0.ftp echo !dir REPORT.CP1C.ROLLEDUP.TXT
      >> %0.ftp echo *************************************************   
      >> %0.ftp echo !dir CONTENT.CP1C.ROLLEDUP.TXT
      >> %0.ftp echo get CONTENT.CP1C.ROLLEDUP.TXT
      >> %0.ftp echo !dir CONTENT.CP1C.ROLLEDUP.TXT
      >> %0.ftp echo *************************************************   
      >> %0.ftp echo !dir WORKLOAD.CP1c.ROLLEDUP.TXT
      >> %0.ftp echo get WORKLOAD.CP1C.ROLLEDUP.TXT
      >> %0.ftp echo !dir WORKLOAD.CP1C.ROLLEDUP.TXT
      >> %0.ftp echo *************************************************   
      >> %0.ftp echo !dir REPORT.TMMC.ROLLEDUP.TXT
      >> %0.ftp echo get REPORT.TMMC.ROLLEDUP.TXT
      >> %0.ftp echo !dir REPORT.TMMC.ROLLEDUP.TXT
      >> %0.ftp echo *************************************************   
      >> %0.ftp echo !dir CONTENT.TMMC.ROLLEDUP.TXT
      >> %0.ftp echo get CONTENT.TMMC.ROLLEDUP.TXT
      >> %0.ftp echo !dir CONTENT.TMMC.ROLLEDUP.TXT
      >> %0.ftp echo **************************************************   
      >> %0.ftp echo !dir WORKLOAD.TMMC.ROLLEDUP.TXT
      >> %0.ftp echo get WORKLOAD.TMMC.ROLLEDUP.TXT
      >> %0.ftp echo !dir WORKLOAD.TMMC.ROLLEDUP.TXT
      >> %0.ftp echo quit
      ftp -d -v -s:%0.ftp 150.45.12.18 > %0.log
      type %0.bat 
      type %0.ftp 
      type %0.log 
      

      【讨论】:

        【解决方案7】:

        我有done this with PowerShell:

        function DownloadFromFtp($destination, $ftp_uri, $user, $pass){
            $dirs = GetDirecoryTree $ftp_uri $user $pass
        
            foreach($dir in $dirs){
               $path = [io.path]::Combine($destination,$dir)
        
               if ((Test-Path $path) -eq $false) {
                  "Creating $path ..."
                  New-Item -Path $path -ItemType Directory | Out-Null
               }else{
                  "Exists $path ..."
               }
            }
        
            $files = GetFilesTree $ftp_uri $user $pass
        
            foreach($file in $files){
                $source = [io.path]::Combine($ftp_uri,$file)
                $dest = [io.path]::Combine($destination,$file)
        
                "Downloading $source ..."
                Get-FTPFile $source $dest $user $pass
            }
        }
        
        function UploadToFtp($artifacts, $ftp_uri, $user, $pass){
            $webclient = New-Object System.Net.WebClient 
            $webclient.Credentials = New-Object System.Net.NetworkCredential($user,$pass)  
        
            foreach($item in Get-ChildItem -recurse $artifacts){ 
        
                $relpath = [system.io.path]::GetFullPath($item.FullName).SubString([system.io.path]::GetFullPath($artifacts).Length + 1)
        
                if ($item.Attributes -eq "Directory"){
        
                    try{
                        Write-Host Creating $item.Name
        
                        $makeDirectory = [System.Net.WebRequest]::Create($ftp_uri+$relpath);
                        $makeDirectory.Credentials = New-Object System.Net.NetworkCredential($user,$pass) 
                        $makeDirectory.Method = [System.Net.WebRequestMethods+FTP]::MakeDirectory;
                        $makeDirectory.GetResponse();
        
                    }catch [Net.WebException] {
                        Write-Host $item.Name probably exists ...
                    }
        
                    continue;
                }
        
                "Uploading $item..."
                $uri = New-Object System.Uri($ftp_uri+$relpath) 
                $webclient.UploadFile($uri, $item.FullName)
            }
        }
        
         function Get-FTPFile ($Source,$Target,$UserName,$Password) 
         { 
             $ftprequest = [System.Net.FtpWebRequest]::create($Source) 
             $ftprequest.Credentials = New-Object System.Net.NetworkCredential($username,$password) 
             $ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::DownloadFile 
             $ftprequest.UseBinary = $true 
             $ftprequest.KeepAlive = $false 
        
             $ftpresponse = $ftprequest.GetResponse() 
             $responsestream = $ftpresponse.GetResponseStream() 
        
             $targetfile = New-Object IO.FileStream ($Target,[IO.FileMode]::Create) 
             [byte[]]$readbuffer = New-Object byte[] 1024 
        
             do{ 
                 $readlength = $responsestream.Read($readbuffer,0,1024) 
                 $targetfile.Write($readbuffer,0,$readlength) 
             } 
             while ($readlength -ne 0) 
        
             $targetfile.close() 
         } 
        
        #task ListFiles {
        #   
        #    $files = GetFilesTree 'ftp://127.0.0.1/' "web" "web"
        #    $files | ForEach-Object {Write-Host $_ -foregroundcolor cyan}
        #}
        
        function GetDirecoryTree($ftp, $user, $pass){
            $creds = New-Object System.Net.NetworkCredential($user,$pass)
        
            $files = New-Object "system.collections.generic.list[string]"
            $folders = New-Object "system.collections.generic.queue[string]"
            $folders.Enqueue($ftp)
        
            while($folders.Count -gt 0){
                $fld = $folders.Dequeue()
        
                $newFiles = GetAllFiles $creds $fld
                $dirs = GetDirectories $creds $fld
        
                foreach ($line in $dirs){
                    $dir = @($newFiles | Where { $line.EndsWith($_) })[0]
                    [void]$newFiles.Remove($dir)
                    $folders.Enqueue($fld + $dir + "/")
        
                    [void]$files.Add($fld.Replace($ftp, "") + $dir + "/")
                }
            }
        
            return ,$files
        }
        
        function GetFilesTree($ftp, $user, $pass){
            $creds = New-Object System.Net.NetworkCredential($user,$pass)
        
            $files = New-Object "system.collections.generic.list[string]"
            $folders = New-Object "system.collections.generic.queue[string]"
            $folders.Enqueue($ftp)
        
            while($folders.Count -gt 0){
                $fld = $folders.Dequeue()
        
                $newFiles = GetAllFiles $creds $fld
                $dirs = GetDirectories $creds $fld
        
                foreach ($line in $dirs){
                    $dir = @($newFiles | Where { $line.EndsWith($_) })[0]
                    [void]$newFiles.Remove($dir)
                    $folders.Enqueue($fld + $dir + "/")
                }
        
                $newFiles | ForEach-Object { 
                    $files.Add($fld.Replace($ftp, "") + $_) 
                }
            }
        
            return ,$files
        }
        
        function GetDirectories($creds, $fld){
            $dirs = New-Object "system.collections.generic.list[string]"
        
            $operation = [System.Net.WebRequestMethods+Ftp]::ListDirectoryDetails
            $reader = GetStream $creds $fld $operation
            while (($line = $reader.ReadLine()) -ne $null) {
        
               if ($line.Trim().ToLower().StartsWith("d") -or $line.Contains(" <DIR> ")) {
                    [void]$dirs.Add($line)
                }
            }
            $reader.Dispose();
        
            return ,$dirs
        }
        
        function GetAllFiles($creds, $fld){
            $newFiles = New-Object "system.collections.generic.list[string]"
            $operation = [System.Net.WebRequestMethods+Ftp]::ListDirectory
        
            $reader = GetStream $creds $fld $operation
        
            while (($line = $reader.ReadLine()) -ne $null) {
               [void]$newFiles.Add($line.Trim()) 
            }
            $reader.Dispose();
        
            return ,$newFiles
        }
        
        function GetStream($creds, $url, $meth){
        
            $ftp = [System.Net.WebRequest]::Create($url)
            $ftp.Credentials = $creds
            $ftp.Method = $meth
            $response = $ftp.GetResponse()
        
            return New-Object IO.StreamReader $response.GetResponseStream()
        }
        
        Export-ModuleMember UploadToFtp, DownLoadFromFtp
        

        【讨论】:

          【解决方案8】:

          手动尝试:

          $ ftp www.domainhere.com 
          > useridhere
          > passwordhere
          > put test.txt
          > bye
          > pause
          

          【讨论】:

          • 此答案与问题本身没有什么不同,因此应将其删除。
          【解决方案9】:

          批处理文件不是这样工作的。他们不只是“输入”所有内容——他们运行系统命令,在这种情况下为ftp,等待它们返回,然后运行下一个命令......所以在这种情况下,解释器只是在等待ftp退出。

          如果您必须使用ftp 命令,则准备一个脚本文件(例如,commands.txt 并运行ftp -s:commands.txt

          但使用 cURL 或 PHP/Perl/Python/其他脚本可能是更好的主意。

          【讨论】:

            最近更新 更多