You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在PowerShell SFTP文件监控脚本中添加上传后本地文件转移功能

实现SFTP上传成功后移动本地文件的PowerShell脚本修正

问题说明

我使用一段监控本地文件夹并自动上传文件到SFTP服务器的PowerShell脚本,需要新增上传成功后将本地文件移动到指定备份目录的功能,但自行添加的代码无法正常运行。

原脚本

# Watches a directory for new files and
# fires off the batch file to push to connection

$remotePath = "/data"
$localPath = "C:\pathTo\watchfolder"
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = $localPath
$watcher.Filter  = "*.csv"
$watcher.IncludeSubdirectories = $True
$watcher.EnableRaisingEvents = $True

### LISTEN FOR CREATE
Register-ObjectEvent $watcher Created -SourceIdentifier FileCreated -Action { 
    try
    {
        $localFilePath = $event.SourceEventArgs.FullPath
        Write-Host "Local path: $localFilePath"

        $assemblyPath = "C:\Program Files (x86)\WinSCP"
        Add-Type -Path (Join-Path $assemblyPath "WinSCPnet.dll")

        # Setup session options
        $sessionOptions = New-Object WinSCP.SessionOptions -Property @{
            Protocol = [WinSCP.Protocol]::Sftp
            HostName = "example.com"
            UserName = "username"
            Password = "password"
            SshHostKeyFingerprint = "ssh-rsa 2048 xxxxxxxxxxx...="
        }

        $session = New-Object WinSCP.Session

        try
        {
            $remoteFilePath =
                [WinSCP.RemotePath]::TranslateLocalPathToRemote(
                    $localFilePath, $localPath, $remotePath)
            Write-Host "Remote path: $remoteFilePath"

            # Connect
            $session.Open($sessionOptions)

            # Check if corresponding remote directory exists, if not, create it
            $i = $remoteFilePath.LastIndexOf("/")
            $remoteDirPath = $remoteFilePath.SubString(0, $i)
            if (($remoteDirPath.Length -gt 0) -and
                !$session.FileExists($remoteDirPath))
            {
                Write-Host "New subdirectory, creating $remoteDirPath on server"
                $session.CreateDirectory($remoteDirPath)
            }

            $session.PutFiles($localFilePath, $remoteFilePath).Check()

            Write-Host "Upload of $localFilePath succeeded"
        }
        finally
        {
            # Disconnect, clean up
            $session.Dispose()
        }

    } #end of first try 
    catch
    {
        Write-Host "Error: $($_.Exception.Message)"
    }
} #end of action


while ($True) { sleep 5 }

尝试的无效代码

# Upload files, collect results
        $transferResult = $session.PutFiles($localPath, $remotePath)
 
        # Iterate over every transfer
        foreach ($transfer in $transferResult.Transfers)
        {
            # Success or error?
            if ($transfer.Error -eq $Null)
            {
                Write-Host "Upload of $($transfer.FileName) succeeded, moving to backup"
                # Upload succeeded, move source file to backup
                Move-Item $transfer.FileName $backupPath
            }
            else
            {
                Write-Host "Upload of $($transfer.FileName) failed: $($transfer.Error.Message)"
            }

修正方案

问题分析

原尝试代码的问题:

  • 未定义$backupPath变量,移动操作无目标路径
  • 使用$localPath(整个监控目录)作为上传参数,而非触发事件的单个文件路径
  • 代码结构不完整,缺少foreach和try块的闭合语句
  • 未处理文件可能被占用的情况(FileSystemWatcher的Created事件可能在文件写入完成前触发)

修正后的完整脚本

# 监控目录并上传文件到SFTP服务器,上传成功后移动文件到备份目录
$remotePath = "/data"
$localPath = "C:\pathTo\watchfolder"
$backupPath = "C:\pathTo\backupfolder" # 新增:定义备份目录

# 创建文件监控器
$watcher = New-Object System.IO.FileSystemWatcher
$watcher.Path = $localPath
$watcher.Filter  = "*.csv"
$watcher.IncludeSubdirectories = $True
$watcher.EnableRaisingEvents = $True

# 注册文件创建事件
Register-ObjectEvent $watcher Created -SourceIdentifier FileCreated -Action { 
    try
    {
        $localFilePath = $event.SourceEventArgs.FullPath
        # 等待文件写入完成,避免文件占用
        Start-Sleep -Seconds 1
        Write-Host "检测到文件: $localFilePath"

        $assemblyPath = "C:\Program Files (x86)\WinSCP"
        Add-Type -Path (Join-Path $assemblyPath "WinSCPnet.dll")

        # SFTP会话配置
        $sessionOptions = New-Object WinSCP.SessionOptions -Property @{
            Protocol = [WinSCP.Protocol]::Sftp
            HostName = "example.com"
            UserName = "username"
            Password = "password"
            SshHostKeyFingerprint = "ssh-rsa 2048 xxxxxxxxxxx...="
        }

        $session = New-Object WinSCP.Session

        try
        {
            $remoteFilePath = [WinSCP.RemotePath]::TranslateLocalPathToRemote($localFilePath, $using:localPath, $using:remotePath)
            Write-Host "远程目标路径: $remoteFilePath"

            # 连接SFTP服务器
            $session.Open($sessionOptions)

            # 创建远程目录(如果不存在)
            $remoteDirPath = $remoteFilePath.SubString(0, $remoteFilePath.LastIndexOf("/"))
            if (($remoteDirPath.Length -gt 0) -and !$session.FileExists($remoteDirPath))
            {
                Write-Host "创建远程目录: $remoteDirPath"
                $session.CreateDirectory($remoteDirPath)
            }

            # 执行上传并捕获结果
            $transferResult = $session.PutFiles($localFilePath, $remoteFilePath)
            # 检查上传是否有错误
            $transferResult.Check()

            # 遍历上传结果,处理成功的文件
            foreach ($transfer in $transferResult.Transfers)
            {
                if ($transfer.Error -eq $null)
                {
                    Write-Host "上传成功: $($transfer.FileName),准备移动到备份目录"
                    # 确保备份目录存在
                    if (-not (Test-Path $using:backupPath)) { New-Item -ItemType Directory -Path $using:backupPath | Out-Null }
                    # 移动文件到备份目录
                    Move-Item -Path $transfer.FileName -Destination $using:backupPath -Force
                    Write-Host "文件已移动到备份目录: $using:backupPath"
                }
                else
                {
                    Write-Host "上传失败: $($transfer.FileName),错误信息: $($transfer.Error.Message)"
                }
            }
        }
        finally
        {
            # 清理会话
            $session.Dispose()
        }

    }
    catch
    {
        Write-Host "执行错误: $($_.Exception.Message)"
    }
}

# 保持脚本运行
while ($True) { sleep 5 }

关键修改点

  1. 新增备份目录定义:在脚本开头添加$backupPath,指定文件移动的目标路径
  2. 处理变量作用域:在事件Action中使用$using:前缀引用外部定义的$localPath、$remotePath、$backupPath(因为Action块有独立的作用域)
  3. 文件写入等待:添加Start-Sleep -Seconds 1避免文件还在写入时触发上传,导致文件占用错误
  4. 完整处理上传结果:捕获PutFiles的返回结果,遍历每个传输项判断是否成功,再执行移动操作
  5. 备份目录检查:移动前检查备份目录是否存在,不存在则自动创建

内容的提问来源于stack exchange,提问作者Matthew L

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 03:35:54