Загрузка данных


<#
.SYNOPSIS
    Reliable folder copy via robocopy, list from file, UTF-8 safe.
#>

[CmdletBinding()]
param(
    [Parameter(Mandatory)] [string]$ListFile,
    [Parameter(Mandatory)] [string]$DestinationRoot,
    [string]$LogDir = (Join-Path $DestinationRoot '_logs'),
    [switch]$Mirror,
    [switch]$CopyAll,
    [switch]$WhatIf,
    [ValidateSet('UTF8','ANSI','Auto')]
    [string]$ListEncoding = 'Auto'
)

# --- Force UTF-8 console ---
try { chcp 65001 | Out-Null } catch {}
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
[Console]::InputEncoding  = [System.Text.Encoding]::UTF8
$OutputEncoding           = [System.Text.Encoding]::UTF8

$ErrorActionPreference = 'Stop'

if (-not (Test-Path -LiteralPath $ListFile)) {
    throw "List file not found: $ListFile"
}
if (-not (Test-Path -LiteralPath $DestinationRoot)) {
    New-Item -ItemType Directory -Force -Path $DestinationRoot | Out-Null
}
if (-not (Test-Path -LiteralPath $LogDir)) {
    New-Item -ItemType Directory -Force -Path $LogDir | Out-Null
}

# --- Read list with encoding fallback ---
function Get-PathList {
    param([string]$File, [string]$Mode)

    $bytes = [System.IO.File]::ReadAllBytes($File)

    $candidates = switch ($Mode) {
        'UTF8' { @([System.Text.Encoding]::UTF8) }
        'ANSI' { @([System.Text.Encoding]::GetEncoding(1251)) }
        'Auto' {
            @(
                [System.Text.Encoding]::UTF8,
                [System.Text.Encoding]::GetEncoding(1251),
                [System.Text.Encoding]::GetEncoding(866)
            )
        }
    }

    $bestLines = $null
    $bestOk    = -1

    foreach ($enc in $candidates) {
        $text = $enc.GetString($bytes)
        $text = $text -replace '^\uFEFF',''   # strip BOM
        $lines = $text -split "`r?`n" |
            ForEach-Object { $_.Trim() } |
            Where-Object { $_ -and -not $_.StartsWith('#') }

        if (-not $lines) { continue }

        $ok = 0
        foreach ($p in $lines) {
            try { if (Test-Path -LiteralPath $p) { $ok++ } } catch {}
        }

        Write-Host ("Encoding {0}: {1}/{2} paths exist" -f $enc.WebName, $ok, $lines.Count) -ForegroundColor DarkGray

        if ($ok -eq $lines.Count) { return $lines }
        if ($ok -gt $bestOk) { $bestOk = $ok; $bestLines = $lines }
    }

    if ($bestLines) { return $bestLines }
    throw "Could not read any paths from $File"
}

$paths = Get-PathList -File $ListFile -Mode $ListEncoding

# --- robocopy common args ---
$commonArgs = @('/R:3','/W:5','/MT:16','/XJ','/NP','/TEE','/NFL','/NDL')
$copyMode = if ($CopyAll) { '/COPYALL' } else { '/COPY:DAT' }
$mode     = if ($Mirror)   { '/MIR' }     else { '/E' }
if ($WhatIf) { $commonArgs += '/L' }

Write-Host ("Folders in list: {0}" -f $paths.Count) -ForegroundColor Cyan
Write-Host ("Destination:     {0}" -f $DestinationRoot) -ForegroundColor Cyan
Write-Host ("Logs:            {0}" -f $LogDir) -ForegroundColor Cyan
Write-Host ("Mode:            {0} {1}{2}" -f $mode, $copyMode, $(if($WhatIf){' /L (dry-run)'}else{''})) -ForegroundColor Cyan
Write-Host ""

$ok = 0
$failed = New-Object System.Collections.Generic.List[string]

foreach ($src in $paths) {
    try {
        $srcFull = [System.IO.Path]::GetFullPath($src).TrimEnd('\')
    } catch {
        Write-Warning "Invalid path: $src"
        $failed.Add($src); continue
    }

    if (-not (Test-Path -LiteralPath $srcFull)) {
        Write-Warning "Source not found: $srcFull"
        $failed.Add($srcFull); continue
    }

    $drive  = ($srcFull -replace '^([A-Za-z]):.*$', '$1').ToUpper()
    $tail   = $srcFull -replace '^[A-Za-z]:\\?', ''
    $target = Join-Path (Join-Path $DestinationRoot $drive) $tail

    $safeName = ($srcFull -replace '^[A-Za-z]:\\?','' -replace '[\\/:*?"<>|]','_')
    if (-not $safeName) { $safeName = $drive }
    $log = Join-Path $LogDir ("{0}_{1}.log" -f $drive, $safeName)

    Write-Host ("-> {0}" -f $srcFull) -ForegroundColor Yellow
    Write-Host ("   {0} -> {1}" -f $mode, $target) -ForegroundColor DarkGray

    $rcArgs = @($srcFull, $target, $mode, $copyMode) + $commonArgs + @("/LOG:$log")
    & robocopy @rcArgs | Out-Null
    $code = $LASTEXITCODE

    if ($code -ge 8) {
        Write-Warning ("FAIL (code {0}): {1} -> log: {2}" -f $code, $srcFull, $log)
        $failed.Add($srcFull)
    } else {
        Write-Host ("   OK (code {0})" -f $code) -ForegroundColor Green
        $ok++
    }
    Write-Host ""
}

Write-Host "==================================================" -ForegroundColor Cyan
Write-Host ("OK:      {0} / {1}" -f $ok, $paths.Count) -ForegroundColor Green
if ($failed.Count -gt 0) {
    Write-Host ("Failed:  {0}" -f $failed.Count) -ForegroundColor Red
    $failed | ForEach-Object { Write-Host ("  - {0}" -f $_) -ForegroundColor Red }
    exit 1
} else {
    Write-Host "All folders copied." -ForegroundColor Green
    exit 0
}