如何在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 }
关键修改点
- 新增备份目录定义:在脚本开头添加
$backupPath,指定文件移动的目标路径 - 处理变量作用域:在事件Action中使用
$using:前缀引用外部定义的$localPath、$remotePath、$backupPath(因为Action块有独立的作用域) - 文件写入等待:添加
Start-Sleep -Seconds 1避免文件还在写入时触发上传,导致文件占用错误 - 完整处理上传结果:捕获
PutFiles的返回结果,遍历每个传输项判断是否成功,再执行移动操作 - 备份目录检查:移动前检查备份目录是否存在,不存在则自动创建
内容的提问来源于stack exchange,提问作者Matthew L
相关产品推荐
相关产品推荐

