How to compare age of local file with file on FTP

2019-05-29 21:03发布

问题:

I'm in the process of writing a PowerShell script to help in the process of setting up new PC's for my work. This will hopefully be used by more than just me so I'm trying to think of everything.

I have offline installers (java, flash, reader, etc) saved on our FTP server that the script downloads if a local copy hasn't already been saved in the Apps directory that gets created. Periodically the files on the FTP server will get updated as new versions of the programs are released. I want the script to have an option of checking for newer versions of the installers in case someone likes to carry around the local copies and forgets to check the server every now and then. It also will need to work in Windows 7 without any need to import additional modules unless there's an easy way to do that on multiple PC's at a time. I know about the import command, but the experiences I've had needed me to copy the module files into multiple places on the PC before it'd work.

Right now I haven't had much luck finding any solutions. I've found code that checks for modified dates on local files, or files on a local server, but nothing that deals with FTP other than uploading\downloading files.

Here's the last thing I tried. I tried a combination of what I found for local files with FTP. Didn't work too well.

I'm new to PowerShell, but I've been pretty good at piecing this whole thing together so far. However, this idea is becoming troublesome.

Thank you for the help.

$ftpsite = "ftp://ftpsite.com/folder/"

$firefox = (Get-Item $dir\Apps\install_firefox.exe).LastWriteTime.toString("MM/dd/yyyy")

if ($firefoxftp = (Get-ChildItem $ftpsite/install_firefox.exe | Where{$_.LastWriteTime -gt $firefox})) {

    $File = "$dir\Apps\install_firefox.exe"
    $ftp = "ftp://ftpsite.com/folder/install_firefox.exe"
    $webclient = New-Object System.Net.WebClient
    $uri = New-Object System.Uri($ftp)
    $webclient.DownloadFile($uri, $File)

}

UPDATE:

Here's what I have after Martin's help. It kind of works. It downloads the file from FTP, but it's not comparing the remote and local correctly. The remote file returns 20150709140505 and the local file returns 07/09/2015 2:05:05 PM. How do I format one to look like the other before the comparison, and is "-gt" the correct comparison to use?

Thanks!

function update {

    $ftprequest = [System.Net.FtpWebRequest]::Create("ftp://ftpsite.com/Script_Apps/install_firefox.exe")
    $ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::GetDateTimestamp
    $response = $ftprequest.GetResponse().StatusDescription
    $tokens = $response.Split(" ")
    $code = $tokens[0]

    $localfile = (Get-Item "$dir\Apps\install_firefox.exe").LastWriteTimeUtc

    if ($tokens -gt $localfile) {

        write-host "Updating Firefox Installer..."
        $File = "$dir\Apps\install_firefox.exe"
        $ftp = "ftp://ftpsite.com/Script_Apps/install_firefox.exe"
        $webclient = New-Object System.Net.WebClient
        $uri = New-Object System.Uri($ftp)
        $webclient.DownloadFile($uri, $File)

        "Updated Firefox" >> $global:logfile

        mainmenu

    }

    else {
        Write-Host "Local Copy is Newer."
        sleep 3
        mainmenu
    }

}

UPDATE 2:

Seems to be working! Here's the code. Thanks for the help!

function update {

    $ftprequest = [System.Net.FtpWebRequest]::Create("ftp://ftpserver.com/Script_Apps/install_firefox.exe")
    $ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::GetDateTimestamp
    $response = $ftprequest.GetResponse().StatusDescription
    $tokens = $response.Split(" ")
    $code = $tokens[0]

    $localtime = (Get-Item "$dir\Apps\install_firefox.exe").LastWriteTimeUtc

    if ($code -eq 213) {

        $tokens = $tokens[1]
        $localtime = "{0:yyyymmddHHmmss}" -f [datetime]$localtime

    }

    if ($tokens -gt $localtime) {

        write-host "Updating Firefox Installer..."
        $File = "$dir\Apps\install_firefox.exe"
        $ftp = "ftp://ftpserver.com/Script_Apps/install_firefox.exe"
        $webclient = New-Object System.Net.WebClient
        $uri = New-Object System.Uri($ftp)
        $webclient.DownloadFile($uri, $File)

        "Updated Firefox" >> $global:logfile

        mainmenu

    }

    else {
        Write-Host "Local Copy is Newer."
        sleep 3
        mainmenu
    }

}

回答1:

You cannot use the WebClient class to check remote file timestamp.

You can use the FtpWebRequest class with its GetDateTimestamp FTP "method" and parse the UTC timestamp string it returns. The format is specified by RFC 3659 to be YYYYMMDDHHMMSS[.sss].

That would work only if the FTP server supports MDTM command that the method uses under the cover (most servers do, but not all).

$ftprequest = [System.Net.FtpWebRequest]::Create("ftp://ftpsite.com/folder/install_firefox.exe")

$ftprequest.Method = [System.Net.WebRequestMethods+Ftp]::GetDateTimestamp

$response = $ftprequest.GetResponse().StatusDescription

$tokens = $response.Split(" ")

$code = $tokens[0]

if ($code -eq 213)
{
    Write-Host "Timestamp is" $tokens[1]
}
else
{
    Write-Host "Error" $response
}

It would output something like:

Timestamp is 20150709065036

Now you parse it, and compare against a UTC timestamp of a local file:

(Get-Item "install_firefox.exe").LastWriteTimeUtc

Or save yourself some time and use an FTP library/tool that can do this for you.

For example with WinSCP .NET assembly, you can synchronize whole remote folder with installers with a local copy with one call to the Session.SynchronizeDirectories. Or your can limit the synchronization to a single file only.

# Load WinSCP .NET assembly
Add-Type -Path "WinSCPnet.dll"

# Setup session options
$sessionOptions = New-Object WinSCP.SessionOptions
$sessionOptions.Protocol = [WinSCP.Protocol]::Ftp
$sessionOptions.HostName = "ftpsite.com"

$session = New-Object WinSCP.Session

# Connect
$session.Open($sessionOptions)

$transferOptions = New-Object WinSCP.TransferOptions
# Synchronize only this one file.
# If you remove the file mask, all files in the folder are synchronized:
$transferOptions.FileMask = "install_firefox.exe"

$session.SynchronizeDirectories(
    [WinSCP.SynchronizationMode]::Local, "$dir\Apps", "/folder",
    $False, $False, [WinSCP.SynchronizationCriteria]::Time, 
    $transferOptions).Check()

To use the assembly, just extract a contents of .NET assembly package to your script folder. No other installation is needed.

The assembly supports not only the MDTM, but also other alternative methods to retrieve the timestamp.

See also a related Powershell example that shows both the above code and other techniques.

(I'm the author of WinSCP)