File Watcher PowerShell脚本无法自动上传文件至FTP服务器求助
问题描述
运行以下PowerShell脚本后,无法实现新增文件自动上传至FTP服务器的功能,仅得到如下输出:
PS C:\Users\Administrator\Desktop\SCRIPT> .\NEWR4.PS1
已加载WinSCP .NET程序集
请输入您的FTP用户名: ****
请输入您的FTP密码: ********
请输入源目录的UNC路径: \HOPE5\MEDIA
会话选项已设置
会话及调试日志路径已设置
已连接至FTP服务器
文件监视器已设置Id Name PSJobTypeName State HasMoreData Location Command
1 f1cabfda-26c... NotStarted False ...
文件监视器事件已注册
对应的脚本代码:
# Load WinSCP .NET assembly Add-Type -Path "C:\Program Files (x86)\WinSCP\WinSCPnet.dll" Write-Host "Loaded WinSCP .NET assembly" # Prompt for username and password $UserName = Read-Host -Prompt 'Enter your FTP username' $Password = Read-Host -Prompt 'Enter your FTP password' -AsSecureString # Prompt for source directory $SourceDirectory = Read-Host -Prompt 'Enter the UNC path of the source directory' # Setup session options $sessionOptions = New-Object WinSCP.SessionOptions -Property @{ Protocol = [WinSCP.Protocol]::Ftp HostName = "ftp.host.com" UserName = $UserName Password = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)) } Write-Host "Session options set up" $session = New-Object WinSCP.Session # Set session and debug log paths $session.SessionLogPath = "C:\temp\WinSCP.log" $session.DebugLogPath = "C:\temp\WinSCP-debug.log" Write-Host "Session and debug log paths set" try { # Connect $session.Open($sessionOptions) Write-Host "Connected to FTP server" # Watcher $watcher = New-Object System.IO.FileSystemWatcher $watcher.Path = $SourceDirectory $watcher.Filter = "*.*" $watcher.IncludeSubdirectories = $false $watcher.EnableRaisingEvents = $true Write-Host "File watcher set up" $action = { $path = $Event.SourceEventArgs.FullPath $extension = [System.IO.Path]::GetExtension($path) if ($extension -eq ".mxf" -or $extension -eq ".mp4" -or $extension -eq ".xml" -or $extension -eq ".oseq") { if ($path -notlike "*.$TMP") { try { # Check if file is still growing $size = (Get-Item $path).Length Start-Sleep -Seconds 60 $newSize = (Get-Item $path).Length if ($size -eq $newSize) { # Check if file is newer than 1 day $creationTime = (Get-Item $path).CreationTime $timeDifference = New-TimeSpan -Start $creationTime -End (Get-Date) if ($timeDifference.TotalDays -le 1) { # Check if remote directory exists $remotePath = "/$(Split-Path $path -Leaf)" if (-not $session.FileExists($remotePath)) { # Create remote directory $session.CreateDirectory($remotePath) Write-Host ("Created remote directory {0}" -f $remotePath) } # Upload files Write-Host ("Attempting to upload file {0}" -f $path) $transferResult = $session.PutFiles($path, $remotePath) # Throw on any error $transferResult.Check() # Print results foreach ($transfer in $transferResult.Transfers) { Write-Host ("Upload of {0} succeeded" -f $transfer.FileName) Add-Content -Path "C:\temp\WinSCP-success.log" -Value ("Upload of {0} succeeded" -f $transfer.FileName) } } else { Write-Host ("File {0} is older than 1 day, skipping this round" -f $path) } } else { Write-Host ("File {0} is still growing, skipping this round" -f $path) } } catch { Write-Host ("Error: {0}" -f $_.Exception.Message) Add-Content -Path "C:\temp\WinSCP-error.log" -Value ("Error: {0}" -f $_.Exception.Message) if ($_.Exception.InnerException -ne $null) { Write-Host ("Inner Exception: {0}" -f $_.Exception.InnerException.Message) Add-Content -Path "C:\temp\WinSCP-error.log" -Value ("Inner Exception: {0}" -f $_.Exception.InnerException.Message) } } } } } Register-ObjectEvent $watcher "Created" -Action $action Write-Host "File watcher event registered" while ($true) { sleep 5 } } finally { # Disconnect, clean up $session.Dispose() Write-Host "Disconnected from FTP server" }
问题分析与修复
核心问题点
- 变量作用域隔离:
Register-ObjectEvent的-Action脚本块属于独立作用域,无法直接访问主作用域的$session对象,导致上传逻辑无法调用WinSCP会话。 - UNC路径格式错误:输入的
\HOPE5\MEDIA缺少开头的\\,FileSystemWatcher无法识别为有效网络共享路径。 - 远程路径逻辑错误:用文件名创建远程目录,会导致尝试将文件上传到同名目录下,完全不符合上传需求。
- 未定义的临时文件变量:
$TMP未初始化,过滤临时文件的条件无效。 - 事件输出无法实时查看:后台事件作业的
Write-Host内容不会同步到主控制台,错误和成功信息无法及时获取。
修复后的脚本
# 加载WinSCP .NET程序集 Add-Type -Path "C:\Program Files (x86)\WinSCP\WinSCPnet.dll" Write-Host "已加载WinSCP .NET程序集" # 提示输入用户名和密码 $UserName = Read-Host -Prompt '请输入您的FTP用户名' $Password = Read-Host -Prompt '请输入您的FTP密码' -AsSecureString $plainPassword = [Runtime.InteropServices.Marshal]::PtrToStringAuto([Runtime.InteropServices.Marshal]::SecureStringToBSTR($Password)) # 验证并获取正确UNC路径 do { $SourceDirectory = Read-Host -Prompt '请输入源目录的UNC路径(格式:\\服务器\共享)' } while (-not $SourceDirectory.StartsWith('\\')) # 设置FTP会话选项 $sessionOptions = New-Object WinSCP.SessionOptions -Property @{ Protocol = [WinSCP.Protocol]::Ftp HostName = "ftp.host.com" UserName = $UserName Password = $plainPassword } Write-Host "会话选项已设置" # 创建日志目录(不存在则自动创建) $logDir = "C:\temp" if (-not (Test-Path $logDir)) { New-Item -ItemType Directory -Path $logDir | Out-Null } # 初始化WinSCP会话 $session = New-Object WinSCP.Session $session.SessionLogPath = Join-Path $logDir "WinSCP.log" $session.DebugLogPath = Join-Path $logDir "WinSCP-debug.log" Write-Host "会话及调试日志路径已设置" try { # 连接FTP服务器 $session.Open($sessionOptions) Write-Host "已连接至FTP服务器" # 配置文件监视器 $watcher = New-Object System.IO.FileSystemWatcher $watcher.Path = $SourceDirectory $watcher.Filter = "*.*" $watcher.IncludeSubdirectories = $false $watcher.EnableRaisingEvents = $true Write-Host "文件监视器已设置" # 将会话对象存入脚本级变量,供事件脚本块访问 $script:ftpSession = $session # 定义文件创建事件的处理逻辑 $action = { $filePath = $Event.SourceEventArgs.FullPath $fileExt = [System.IO.Path]::GetExtension($filePath).ToLower() $allowedExts = @(".mxf", ".mp4", ".xml", ".oseq") $tmpSuffix = ".tmp" # 过滤允许的文件类型和非临时文件 if ($allowedExts -contains $fileExt -and $filePath -notlike "*$tmpSuffix") { try { # 循环等待文件写入完成(最多等待5分钟) $waitElapsed = 0 $maxWait = 300 do { $currentSize = (Get-Item $filePath).Length Start-Sleep -Seconds 10 $newSize = (Get-Item $filePath).Length $waitElapsed +=10 } while ($currentSize -ne $newSize -and $waitElapsed -lt $maxWait) # 处理写入超时情况 if ($waitElapsed -ge $maxWait) { $logMsg = "文件 $filePath 写入超时,跳过上传" Write-Host $logMsg Add-Content -Path "C:\temp\WinSCP-error.log" -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - $logMsg" return } # 检查文件是否在1天内创建 $createTime = (Get-Item $filePath).CreationTime $timeDiff = New-TimeSpan -Start $createTime -End (Get-Date) if ($timeDiff.TotalDays -gt 1) { $logMsg = "文件 $filePath 已超过1天,跳过上传" Write-Host $logMsg Add-Content -Path "C:\temp\WinSCP-success.log" -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - $logMsg" return } # 设置远程上传路径(FTP根目录,保持原文件名) $fileName = [System.IO.Path]::GetFileName($filePath) $remotePath = "/$fileName" # 检查远程文件是否已存在 if ($script:ftpSession.FileExists($remotePath)) { $logMsg = "远程服务器已存在文件 $fileName,跳过上传" Write-Host $logMsg Add-Content -Path "C:\temp\WinSCP-success.log" -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - $logMsg" return } # 执行文件上传 $logMsg = "开始上传文件 $filePath" Write-Host $logMsg Add-Content -Path "C:\temp\WinSCP-success.log" -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - $logMsg" $transferResult = $script:ftpSession.PutFiles($filePath, $remotePath) $transferResult.Check() # 抛出上传过程中的错误 # 记录上传成功信息 foreach ($transfer in $transferResult.Transfers) { $successMsg = "文件 $($transfer.FileName) 上传成功" Write-Host $successMsg Add-Content -Path "C:\temp\WinSCP-success.log" -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - $successMsg" } } catch { # 记录错误信息 $errorMsg = "上传错误: $($_.Exception.Message)" Write-Host $errorMsg -ForegroundColor Red Add-Content -Path "C:\temp\WinSCP-error.log" -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - $errorMsg" if ($_.Exception.InnerException) { $innerErrMsg = "内部错误: $($_.Exception.InnerException.Message)" Write-Host $innerErrMsg -ForegroundColor Red Add-Content -Path "C:\temp\WinSCP-error.log" -Value "$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss') - $innerErrMsg" } } } } # 注册文件创建事件 $eventJob = Register-ObjectEvent $watcher "Created" -Action $action Write-Host "文件监视器事件已注册" # 保持脚本运行,同步显示事件输出 Write-Host "脚本正在运行,按Ctrl+C停止..." while ($true) { if ($eventJob.HasMoreData) { Receive-Job $eventJob } Start-Sleep -Seconds 5 } } finally { # 清理资源 if ($watcher) { $watcher.Dispose() } if ($session) { $session.Dispose() Write-Host "已断开与FTP服务器的连接" } if ($eventJob) { Unregister-Event $eventJob.Id } }
关键修复说明
- 作用域处理:通过
$script:ftpSession将主作用域的会话对象暴露给事件脚本块,解决跨作用域访问问题。 - 路径验证:强制输入符合格式的UNC路径,避免无效路径导致监视器无法工作。
- 远程路径修正:直接使用文件名作为远程路径,确保文件正确上传到FTP根目录(可根据需求修改为指定目录)。
- 文件写入等待优化:用循环检查文件大小替代固定等待,更高效适配不同大小的文件。
- 日志增强:添加时间戳,便于问题追溯;同步事件输出到主控制台,实时查看运行状态。
内容的提问来源于stack exchange,提问作者David Greenway
相关产品推荐
相关产品推荐

