Search code examples
powershellloopsftpftpwebrequest

Timeout FTP upload with FtpWebRequest when it stalls


So I have a simple FTP upload script that loops through a set of files to upload. The loop picks a file (sorted gci array) uploads it, and then moves on to the next. A total of about 20 files. The loop for a single file ends when either I get an error from the server or when I get confirmation from the server that an upload is done.

Recently, that server has been giving me neither, allowing the upload to go through, but not giving a confirmation that a file has finished. Essentially meaning the script just get's stuck in that loop for hours until I shut off the script manually.

My hope is to put a timeout on the main upload command which is:

$rs = $ftp.GetRequestStream()
$rs.Write($content, 0, $content.Length)

It seems as though the write command, though the file is fully written on the destination FTP server, never gets a response back from the FTP server, meaning it'll just hang and hang and hang.

What I'm hoping to do is add a timeout. The challenge here is, I don't know of a way to check for timers in the middle of executing a "write" job.

Do I have an option here?

Here's what I'm going with but it seems like (my guess) because the timers are never being compared against each other in the middle of the write job, the loop won't exit until the re.write job finishes (which could be never)

Any ideas here?

$timeout = New-TimeSpan -Seconds 8
$timer = [System.Diagnostics.Stopwatch]::StartNew()
do {
    # create the FtpWebRequest and configure it
    $ftp = [System.Net.FtpWebRequest]::Create($ftpdestination)
    $ftp = [System.Net.FtpWebRequest]$ftp
    # build authentication and connection
    $ftp.Method = [System.Net.WebRequestMethods+Ftp]::UploadFile
    $ftp.Credentials = new-object System.Net.NetworkCredential($username,$password)
    $ftp.UseBinary = $true
    $ftp.UsePassive = $true
    $ftp.timeout = -1
    # read in the file to upload as a byte array
    $content = [System.IO.File]::ReadAllBytes($sourcefile)
    $ftp.ContentLength = $content.Length
    # get the request stream, and write the bytes into it
    $rs = $ftp.GetRequestStream()
    $rs.Write($content, 0, $content.Length)
    $rs.Close()
    $rs.Dispose()
}
while ($timer.Elapsed -lt $timeout)

So that's what I'm using, however, I get that the problem is that during rs.write (where all my time is going) it's not reevaluating the ($timer.Elapsed -lt $timeout) equation, so it's not stopping. That's where I'm stuck.


Solution

  • Your problem is the $ftp.timeout = -1. The -1 means "infinite". See the Remarks section in FtpWebRequest.Timeout property documentation

    Set it to some sensible value.