Загрузка данных
# --- Настройки ---
$ftpHost = "10.0.2.15"
$ftpPath = "/AisNalog3"
$ftpUser = "anonymous"
$ftpPassword = ""
$baseDir = $PSScriptRoot
if (-not $baseDir) { $baseDir = Split-Path -Parent $PSCommandPath }
if (-not $baseDir) { $baseDir = Get-Location }
$localBasePath = $baseDir
$networkBasePath = "Z:\App"
$flagFile = Join-Path $networkBasePath "success.flag"
$folderMapping = @{
"Prom" = "KPE"
"PredProm" = "PPK"
"OE" = "KOE"
"OEIZ" = "KOEIZ"
}
$versionPattern = '^\d+_\d+_\d+_\d+$'
# --- Лог ---
$logFile = Join-Path $baseDir "ftp_pipeline.log"
function Write-PipeLog {
param([string]$Message, [string]$Level = "INFO")
$line = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] [$Level] $Message"
$color = switch ($Level) {
"ERROR" { "Red" }
"WARN" { "Yellow" }
"OK" { "Green" }
"QUEUE" { "Magenta" }
default { "Gray" }
}
Write-Host $line -ForegroundColor $color
Add-Content -Path $logFile -Value $line -Encoding UTF8 -ErrorAction SilentlyContinue
}
Write-PipeLog "FTP: $ftpHost$ftpPath"
Write-PipeLog "Локально: $localBasePath"
Write-PipeLog "Сеть: $networkBasePath"
Write-PipeLog "Лог: $logFile"
# Удаляем старый флаг
if (Test-Path $flagFile) {
Write-PipeLog "Удаляю старый success.flag" "WARN"
Remove-Item $flagFile -Force -ErrorAction SilentlyContinue
}
# Чистим старые .ready и .skipped от прошлых запусков
Get-ChildItem -Path $localBasePath -Filter "*.ready" -File -ErrorAction SilentlyContinue | Remove-Item -Force
Get-ChildItem -Path $localBasePath -Filter "*.skipped" -File -ErrorAction SilentlyContinue | Remove-Item -Force
# JOB 1: FTP WORKER
$ftpJob = Start-Job -Name "FTPWorker" -ScriptBlock {
param($ftpHost, $ftpPath, $ftpUser, $ftpPassword,
$localBasePath, $networkBasePath, $folderMapping,
$versionPattern, $logFile)
function Write-Log {
param([string]$Message, [string]$Level = "INFO")
$line = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] [FTP] [$Level] $Message"
Write-Host $line -ForegroundColor $(switch ($Level) {
"ERROR" { "Red" }; "WARN" { "Yellow" }; "OK" { "Green" }; default { "Gray" }
})
Add-Content -Path $logFile -Value $line -Encoding UTF8 -ErrorAction SilentlyContinue
}
function Get-FtpFoldersWithDates {
param([string]$ftpPath)
$folders = @{}
try {
$uri = "ftp://$ftpHost$ftpPath/"
$request = [System.Net.FtpWebRequest]::Create($uri)
$request.Method = [System.Net.WebRequestMethods+Ftp]::ListDirectoryDetails
$request.Credentials = New-Object System.Net.NetworkCredential($ftpUser, $ftpPassword)
$request.UseBinary = $true; $request.UsePassive = $true
$request.EnableSsl = $false; $request.KeepAlive = $false
$request.Timeout = 30000
$response = $request.GetResponse()
$stream = $response.GetResponseStream()
$reader = New-Object System.IO.StreamReader($stream)
$listing = $reader.ReadToEnd()
$reader.Close(); $response.Close()
$lines = $listing -split "`r?`n" | Where-Object { $_ -ne "" }
foreach ($line in $lines) {
$isDirectory = $false; $folderName = $null; $dateTime = $null
if ($line -match "^d.*\s+(\S+)$") {
$folderName = $matches[1]; $isDirectory = $true
if ($line -match "(\w{3})\s+(\d{1,2})\s+(\d{2}:\d{2})") {
$month = $matches[1]; $day = $matches[2]; $time = $matches[3]
$year = (Get-Date).Year
try { $dateTime = [DateTime]::ParseExact("$month $day $year $time", "MMM d yyyy HH:mm", [System.Globalization.CultureInfo]::InvariantCulture) }
catch { try { $dateTime = [DateTime]::ParseExact("$month $day $year $time", "MMM dd yyyy HH:mm", [System.Globalization.CultureInfo]::InvariantCulture) } catch { } }
}
} elseif ($line -match "<DIR>\s+(\S+)$") {
$folderName = $matches[1]; $isDirectory = $true
if ($line -match "(\d{2}-\d{2}-\d{2})\s+(\d{2}:\d{2}[AP]M)") {
try { $dateTime = [DateTime]::ParseExact("$($matches[1]) $($matches[2])", "MM-dd-yy hh:mmtt", [System.Globalization.CultureInfo]::InvariantCulture) } catch { }
}
}
if ($isDirectory -and $folderName -and $folderName -ne "." -and $folderName -ne "..") {
if ($dateTime) { $folders[$folderName] = $dateTime } else { $folders[$folderName] = [DateTime]::MinValue }
}
}
return $folders
} catch { Write-Log "Ошибка списка папок: $_" "ERROR"; return @{} }
}
function Get-FtpFileList {
param([string]$ftpPath)
$items = @()
try {
$uri = "ftp://$ftpHost$ftpPath/"
$request = [System.Net.FtpWebRequest]::Create($uri)
$request.Method = [System.Net.WebRequestMethods+Ftp]::ListDirectory
$request.Credentials = New-Object System.Net.NetworkCredential($ftpUser, $ftpPassword)
$request.UseBinary = $true; $request.UsePassive = $true
$request.EnableSsl = $false; $request.KeepAlive = $false
$request.Timeout = 30000
$response = $request.GetResponse()
$stream = $response.GetResponseStream()
$reader = New-Object System.IO.StreamReader($stream)
$listing = $reader.ReadToEnd()
$reader.Close(); $response.Close()
$lines = $listing -split "`r?`n" | Where-Object { $_ -ne "" }
foreach ($line in $lines) { $items += $line.Trim() }
return $items
} catch { Write-Log "Ошибка списка файлов: $_" "ERROR"; return @() }
}
function Get-FtpFileSize {
param([string]$remoteUri)
try {
$request = [System.Net.FtpWebRequest]::Create($remoteUri)
$request.Method = [System.Net.WebRequestMethods+Ftp]::GetFileSize
$request.Credentials = New-Object System.Net.NetworkCredential($ftpUser, $ftpPassword)
$request.UseBinary = $true; $request.UsePassive = $true
$request.EnableSsl = $false; $request.KeepAlive = $false
$request.Timeout = 30000
$response = $request.GetResponse()
$size = $response.ContentLength
$response.Close()
if ($size -gt 0) { return [int64]$size }
} catch { }
return -1
}
function Download-FtpFile {
param([string]$remoteUri, [string]$localPath, [string]$fileName)
try {
$request = [System.Net.FtpWebRequest]::Create($remoteUri)
$request.Method = [System.Net.WebRequestMethods+Ftp]::DownloadFile
$request.Credentials = New-Object System.Net.NetworkCredential($ftpUser, $ftpPassword)
$request.UseBinary = $true; $request.UsePassive = $true
$request.EnableSsl = $false; $request.Timeout = 60000
$response = $request.GetResponse()
$ftpStream = $response.GetResponseStream()
$localStream = [System.IO.File]::Create($localPath)
$bufferSize = 4MB
$buffer = New-Object Byte[] $bufferSize
$bytesRead = $ftpStream.Read($buffer, 0, $bufferSize)
$downloaded = 0
while ($bytesRead -gt 0) {
$localStream.Write($buffer, 0, $bytesRead)
$downloaded += $bytesRead
$bytesRead = $ftpStream.Read($buffer, 0, $bufferSize)
}
$localStream.Close(); $ftpStream.Close(); $response.Close()
$mb = [math]::Round($downloaded / 1MB, 2)
Write-Log "Скачан: $fileName ($mb MB)" "OK"
return $true
} catch {
Write-Log "Ошибка скачивания $fileName : $_" "ERROR"
if (Test-Path $localPath) { Remove-Item $localPath -Force -ErrorAction SilentlyContinue }
return $false
}
}
Write-Log "Worker запущен"
foreach ($remoteFolder in $folderMapping.Keys) {
$localFolder = $folderMapping[$remoteFolder]
$remoteBasePath = "$ftpPath/$remoteFolder"
$networkFolder = Join-Path $networkBasePath $localFolder
Write-Log "Обработка: $remoteFolder -> $localFolder"
if (!(Test-Path $networkFolder)) {
try { New-Item -ItemType Directory -Path $networkFolder -Force -ErrorAction Stop | Out-Null }
catch { Write-Log "Не удалось создать $networkFolder : $_" "ERROR"; continue }
}
$foldersWithDates = Get-FtpFoldersWithDates -ftpPath $remoteBasePath
if ($foldersWithDates.Count -eq 0) { Write-Log "$remoteFolder : нет подпапок" "ERROR"; continue }
$latestFolder = $null; $latestDate = [DateTime]::MinValue
foreach ($folder in $foldersWithDates.Keys) {
$date = $foldersWithDates[$folder]
if ($date -gt $latestDate) { $latestDate = $date; $latestFolder = $folder }
}
if (-not $latestFolder) { Write-Log "$remoteFolder : не определена самая новая папка" "ERROR"; continue }
# Проверка версии
$versionFile = $null
Get-ChildItem -Path $networkFolder -File -Force -ErrorAction SilentlyContinue | ForEach-Object {
if ($_.BaseName -match $versionPattern) { $versionFile = $_ }
}
if ($versionFile -and $versionFile.BaseName -eq $latestFolder) {
Write-Log "$remoteFolder : версия актуальна ($latestFolder). Пропускаем." "OK"
"Skipped" | Out-File (Join-Path $localBasePath "$localFolder.skipped") -Encoding UTF8 -Force
continue
}
# Ищем RAR
$ekpPath = "$remoteBasePath/$latestFolder/EKP"
$ekpFiles = Get-FtpFileList -ftpPath $ekpPath
if ($ekpFiles.Count -eq 0) { Write-Log "$remoteFolder : EKP пуста" "ERROR"; continue }
$archives = @()
foreach ($file in $ekpFiles) { if ($file -match "\.rar$") { $archives += $file } }
if ($archives.Count -eq 0) { Write-Log "$remoteFolder : нет RAR" "ERROR"; continue }
$archiveName = $archives[0]
$networkFilePath = Join-Path $networkFolder $archiveName
# Уже на сети?
if (Test-Path $networkFilePath) {
$ftpUri = "ftp://$ftpHost$ekpPath/$archiveName"
$ftpSize = Get-FtpFileSize -remoteUri $ftpUri
$netSize = (Get-Item $networkFilePath).Length
if ($ftpSize -gt 0 -and $ftpSize -eq $netSize) {
Write-Log "$remoteFolder : файл уже полностью на сети. Пропускаем." "OK"
"Skipped" | Out-File (Join-Path $localBasePath "$localFolder.skipped") -Encoding UTF8 -Force
continue
} else {
Write-Log "$remoteFolder : файл на сети неполный. Перекачаем." "WARN"
Remove-Item $networkFilePath -Force -ErrorAction SilentlyContinue
}
}
# Скачиваем локально
$localFilePath = Join-Path $localBasePath $archiveName
if (Test-Path $localFilePath) { Remove-Item $localFilePath -Force -ErrorAction SilentlyContinue }
$downloadUri = "ftp://$ftpHost$ekpPath/$archiveName"
if (-not (Download-FtpFile -remoteUri $downloadUri -localPath $localFilePath -fileName $archiveName)) {
Write-Log "$remoteFolder : ошибка скачивания" "ERROR"
continue
}
# Создаём .ready маркер → SMB Worker увидит и начнёт копировать
$readyMarker = "$localFilePath.ready"
@{
LocalFolder = $localFolder
LatestFolder = $latestFolder
ArchiveName = $archiveName
NetworkFolder = $networkFolder
} | ConvertTo-Json | Out-File $readyMarker -Encoding UTF8 -Force
Write-Log "[QUEUE] Готов к копированию: $archiveName ($localFolder)" "OK"
}
Write-Log "Worker завершён"
} -ArgumentList $ftpHost, $ftpPath, $ftpUser, $ftpPassword,
$localBasePath, $networkBasePath, $folderMapping,
$versionPattern, $logFile
# JOB 2: SMB WORKER
$smbJob = Start-Job -Name "SMBWorker" -ScriptBlock {
param($localBasePath, $versionPattern, $logFile)
function Write-Log {
param([string]$Message, [string]$Level = "INFO")
$line = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] [SMB] [$Level] $Message"
Write-Host $line -ForegroundColor $(switch ($Level) {
"ERROR" { "Red" }; "WARN" { "Yellow" }; "OK" { "Green" }; default { "Gray" }
})
Add-Content -Path $logFile -Value $line -Encoding UTF8 -ErrorAction SilentlyContinue
}
function Copy-FileWithProgress {
param([string]$src, [string]$dst, [string]$fileName)
try {
$sourceStream = [System.IO.File]::OpenRead($src)
$destStream = [System.IO.File]::Create($dst)
$bufferSize = 4MB
$buffer = New-Object Byte[] $bufferSize
$total = $sourceStream.Length
$copied = 0
$lastPercent = -1
try {
while (($bytesRead = $sourceStream.Read($buffer, 0, $bufferSize)) -gt 0) {
$destStream.Write($buffer, 0, $bytesRead)
$copied += $bytesRead
$percent = [math]::Floor(($copied / $total) * 100)
if ($percent -ne $lastPercent) {
$lastPercent = $percent
$mb = [math]::Round($copied / 1MB, 1)
$mbTotal = [math]::Round($total / 1MB, 1)
$filled = [math]::Floor(30 * $percent / 100)
$bar = ("*" * $filled) + ("." * (30 - $filled))
Write-Host "`r [SMB] $fileName : [$bar] $percent% ($mb/$mbTotal MB)" -NoNewline -ForegroundColor Cyan
}
}
} finally {
$sourceStream.Close()
$destStream.Close()
}
Write-Host ""
Write-Log "Скопирован: $fileName" "OK"
return $true
} catch {
Write-Host ""
Write-Log "Ошибка копирования $fileName : $_" "ERROR"
return $false
}
}
function Update-VersionFile {
param([string]$folderPath, [string]$latestFolder)
try {
$versionFile = $null
Get-ChildItem -Path $folderPath -File -Force -ErrorAction SilentlyContinue | ForEach-Object {
if ($_.BaseName -match $versionPattern) { $versionFile = $_ }
}
if ($versionFile) {
if ($versionFile.BaseName -ne $latestFolder) {
$ext = $versionFile.Extension
if ([string]::IsNullOrEmpty($ext)) { $ext = ".txt" }
Rename-Item -Path $versionFile.FullName -NewName "$latestFolder$ext" -Force
Write-Log "Маркер версии обновлён: $latestFolder$ext" "OK"
}
} else {
New-Item -ItemType File -Path (Join-Path $folderPath "$latestFolder.txt") -Force | Out-Null
Write-Log "Маркер версии создан: $latestFolder.txt" "OK"
}
} catch {
Write-Log "Ошибка обновления маркера: $_" "ERROR"
}
}
Write-Log "Worker запущен. Слежу за *.ready"
$processedFiles = @{}
$ftpDoneMarker = Join-Path $localBasePath "ftp.done"
while ($true) {
# Проверяем, закончил ли FTP worker
$ftpDone = Test-Path $ftpDoneMarker
# Ищем .ready файлы
$readyFiles = Get-ChildItem -Path $localBasePath -Filter "*.ready" -File -ErrorAction SilentlyContinue
if ($readyFiles.Count -eq 0) {
if ($ftpDone) {
Write-Log "FTP worker завершён, и .ready больше нет. Выходим."
break
}
Start-Sleep -Seconds 1
continue
}
foreach ($readyFile in $readyFiles) {
if ($processedFiles.ContainsKey($readyFile.FullName)) { continue }
Write-Log "Найден готовый файл: $($readyFile.Name)"
try {
$meta = Get-Content $readyFile.FullName -Raw -Encoding UTF8 | ConvertFrom-Json
} catch {
Write-Log "Не удалось прочитать $($readyFile.FullName): $_" "ERROR"
$processedFiles[$readyFile.FullName] = $true
Remove-Item $readyFile.FullName -Force -ErrorAction SilentlyContinue
continue
}
$localRar = $readyFile.FullName -replace '\.ready$', ''
if (-not (Test-Path $localRar)) {
Write-Log "Локальный файл исчез: $localRar" "ERROR"
$processedFiles[$readyFile.FullName] = $true
Remove-Item $readyFile.FullName -Force -ErrorAction SilentlyContinue
continue
}
$destPath = Join-Path $meta.NetworkFolder $meta.ArchiveName
if (Copy-FileWithProgress -src $localRar -dst $destPath -fileName $meta.ArchiveName) {
# Удаляем локальный .rar и .ready
Remove-Item $localRar -Force -ErrorAction SilentlyContinue
Remove-Item $readyFile.FullName -Force -ErrorAction SilentlyContinue
Write-Log "Удалён локальный: $localRar"
# Обновляем маркер версии на сети
Update-VersionFile -folderPath $meta.NetworkFolder -latestFolder $meta.LatestFolder
}
$processedFiles[$readyFile.FullName] = $true
}
if ($ftpDone -and (Get-ChildItem -Path $localBasePath -Filter "*.ready" -File -ErrorAction SilentlyContinue).Count -eq 0) {
break
}
Start-Sleep -Milliseconds 500
}
Write-Log "Worker завершён"
} -ArgumentList $localBasePath, $versionPattern, $logFile
# ОЖИДАНИЕ ЗАВЕРШЕНИЯ
Write-PipeLog "Запущены job'ы: FTP=$($ftpJob.Id), SMB=$($smbJob.Id)"
Write-PipeLog "Ожидаем завершения FTP worker'а..."
# Ждём FTP job
Wait-Job -Job $ftpJob | Out-Null
Write-PipeLog "FTP worker завершён. Сигналим SMB worker'у."
# Создаём маркер "ftp.done" → SMB worker поймёт, что больше нечего ждать
"SMB может завершаться" | Out-File (Join-Path $localBasePath "ftp.done") -Encoding UTF8 -Force
# Ждём SMB job
Wait-Job -Job $smbJob | Out-Null
Write-PipeLog "SMB worker завершён."
# Показываем вывод job'ов
Write-PipeLog "=== ВЫВОД FTP WORKER ==="
Receive-Job -Job $ftpJob | ForEach-Object { Write-Host $_ }
Write-PipeLog "=== ВЫВОД SMB WORKER ==="
Receive-Job -Job $smbJob | ForEach-Object { Write-Host $_ }
# Удаляем job'ы
Remove-Job -Job $ftpJob -Force
Remove-Job -Job $smbJob -Force
# ПРОВЕРКА РЕЗУЛЬТАТА
Write-PipeLog "========================================"
Write-PipeLog "ПРОВЕРКА РЕЗУЛЬТАТА"
$allOk = $true
foreach ($remoteFolder in $folderMapping.Keys) {
$localFolder = $folderMapping[$remoteFolder]
$networkFolder = Join-Path $networkBasePath $localFolder
$skippedMarker = Join-Path $localBasePath "$localFolder.skipped"
$hasRar = $false
if (Test-Path $networkFolder) {
$rarFiles = Get-ChildItem -Path $networkFolder -Filter "*.rar" -File -ErrorAction SilentlyContinue
$hasRar = $rarFiles.Count -gt 0
}
$wasSkipped = Test-Path $skippedMarker
if ($hasRar -or $wasSkipped) {
$status = if ($wasSkipped) { "УСПЕШНО (пропущено)" } else { "УСПЕШНО (скопировано)" }
Write-PipeLog " $remoteFolder -> $localFolder : $status" "OK"
} else {
Write-PipeLog " $remoteFolder -> $localFolder : ОШИБКА" "ERROR"
$allOk = $false
}
}
# Чистим временные маркеры
Get-ChildItem -Path $localBasePath -Filter "*.skipped" -File -ErrorAction SilentlyContinue | Remove-Item -Force
Remove-Item (Join-Path $localBasePath "ftp.done") -Force -ErrorAction SilentlyContinue
# СОЗДАНИЕ success.flag
if ($allOk) {
try {
if (!(Test-Path $networkBasePath)) {
New-Item -ItemType Directory -Path $networkBasePath -Force | Out-Null
}
$flagContent = "Конвейер завершён: $(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')`r`n"
$flagContent += "Все папки обработаны успешно.`r`n"
$flagContent | Out-File -FilePath $flagFile -Encoding UTF8
Write-PipeLog "Файл success.flag создан: $flagFile" "OK"
} catch {
Write-PipeLog "Ошибка создания success.flag: $_" "ERROR"
}
} else {
Write-PipeLog "Не все папки обработаны. success.flag НЕ создан." "ERROR"
}
Write-PipeLog "========================================"
Write-PipeLog "Скрипт завершён."
Read-Host "123"
exit 0