Große Dateien mit Powershell splitten und wieder zusammenbauen


Aufgrund eines aktuellen Falles, wo große Dateien mittels Teamviewer übertragen werden mussten, die Verbindung aber mehr als instabil war, musste eine alternative Lösung her. Ein anderes Programm für die Datenübertragung zu installieren war aber nicht möglich.

Da bei jedem Abbruch des Teamviewers die Datenübertragung der großen Dateien nochmal von neuem gestartet werden musste, mussten die großen Dateien in möglichst kleine Einheiten umgepackt werden, damit diese, solange die Verbindung stabil war, erfolgreich übertragen werden konnten. Dabei gilt natürlich je kleiner die Pakete desto höher die Wahrscheinlichkeit der erfolgreichen Übertragung vor dem nächsten Abbruch.

Hier eine Funktion, welche den Part mit dem in kleinere Einheiten verpacken übernimmt:

Function Split-File{
    [CmdletBinding()]
    Param(
        [String]$File, 
        [String]$Prefix,
        [Int32] $ChunkSize
    )

    $inFile = Resolve-Path -Path $File -ErrorAction SilentlyContinue
    If (-Not $inFile) {
        throw "Unable to locate $File"
    }

    If (Test-Path $Prefix -IsValid) {
        If (Split-Path $Prefix) {
            $Prefix = Join-Path -Path (Resolve-Path (Split-Path $Prefix -Parent)) -ChildPath (Split-Path $Prefix -Leaf)
        }
    }
    Write-Verbose "InFile $inFile"
    Write-Verbose "OutPrefix $Prefix"

    # calculate amount of chunks
    $chunks = ([Math]::Round((dir $inFile).Length / $ChunkSize))
    $chunkLength = ([string]$chunks).length
    $chunkFormat = "{0,$($chunkLength):$(‚0‘ * $chunkLength )}"
    Write-Verbose "Chunks $chunks"
    Write-Verbose "Chunkformat $chunkFormat"

    $inStream = [System.IO.File]::OpenRead($inFile)
    $chunkNum = 1
    $bytes = [Byte[]] 0*$ChunkSize

    While ( $bytesRead = $inStream.Read($bytes,0,$ChunkSize)) {
       
        $outFile = "$Prefix$chunkFormat" -f $chunkNum
        $outStream = [System.IO.File]::OpenWrite($outFile)
        $outStream.Write($bytes,0,$bytesRead);
        $outStream.Close();
        Write-Verbose "ChunkFile $outFile"
        $chunkNum += 1
    }

    $inStream.Close()
}

Gegeben sei eine große Datei mit Namen GroßeDatei.ZIP mit 1,28GB, diese soll in kleine Einheiten a 100MB umgepackt werden.

Damit kann man z. B. diesen Aufruf durchführen:

Split-File -File .\GroßeDatei.ZIP -Prefix .\KleineDatei -ChunkSize 100MB –Verbose

als Ausgabe erscheint:

AUSFÜHRLICH: InFile C:\temp\GroßeDatei.ZIP
AUSFÜHRLICH: OutPrefix C:\temp\KleineDatei
AUSFÜHRLICH: ChunkFile C:\temp\KleineDatei01
AUSFÜHRLICH: ChunkFile C:\temp\KleineDatei02
AUSFÜHRLICH: ChunkFile C:\temp\KleineDatei03
AUSFÜHRLICH: ChunkFile C:\temp\KleineDatei04
AUSFÜHRLICH: ChunkFile C:\temp\KleineDatei05
AUSFÜHRLICH: ChunkFile C:\temp\KleineDatei06
AUSFÜHRLICH: ChunkFile C:\temp\KleineDatei07
AUSFÜHRLICH: ChunkFile C:\temp\KleineDatei08
AUSFÜHRLICH: ChunkFile C:\temp\KleineDatei09
AUSFÜHRLICH: ChunkFile C:\temp\KleineDatei10
AUSFÜHRLICH: ChunkFile C:\temp\KleineDatei11
AUSFÜHRLICH: ChunkFile C:\temp\KleineDatei12
AUSFÜHRLICH: ChunkFile C:\temp\KleineDatei13
AUSFÜHRLICH: ChunkFile C:\temp\KleineDatei14

Diese 100MB Pakete lassen sich nun wesentlich einfacher Übertragen und wenn doch eine Übertragung nicht klappt, muss nur das betreffende Paket nochmal übertragen werden.

Soweit so gut. Hat man nun alle Pakete auf der anderen Seite, dann muss man die Ursprungsdatei aber auch wieder zusammenbauen.

Dies geht ausnahmsweise ohne Powershell und direkt in der Eingabeaufforderung mittels COPY-Befehl:

Copy /B .\KleineDatei01 + .\KleineDatei02 TestDatei
Copy-Item : Es wurde kein Positionsparameter gefunden, der das Argument "+" akzeptiert.
In Zeile:1 Zeichen:1
+ copy /B .\KleineDatei01 + .\KleineDatei02 TestDatei
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Copy-Item], ParameterBindingException
    + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.CopyItemCommand

Ups oder doch nicht? Die Erklärung ist ganz einfach, Powershell hat einen COPY-Alias (Get-Alias Copy) welcher auf Copy-Item verweist und Copy-Item ist eben nicht Syntaxkompatibel zum reinen Eingabeaufforderung COPY-Befehl. Aber man kann die zugehörige Shell explizit starten:

cmd.exe /C copy /B .\KleineDatei01 + .\KleineDatei02 + …  GroßeDatei.ZIP
.\KleineDatei01
.\KleineDatei02

        1 Datei(en) kopiert.

Damit ist wieder alles so wie es ursprünglich auf der anderen Seite war. Es gibt noch alternative Methoden die Dateien zusammenzubauen aber da spielt auch die Performancefrage eine Rolle: http://stackoverflow.com/questions/1783554/fast-and-simple-binary-concatenate-files-in-powershell.

Wenn man aber bei der einfachen Shell-Copy-Methode bleibt, dann kann es ziemlich mühsam werden, wenn man mehr als 5 Dateien zusammenbauen muss. Deshalb hier noch eine kleine Hilfe mittels Powershell:

(dir kleinedate*).name -join ‚ + .\‘

# Unter Powershell 2.0 verwendet man
(dir kleinedate* | select -ExpandProperty Name) -Join ‚+ .\‘

erzeugt:

KleineDatei01 + .\KleineDatei02 + .\KleineDatei03 + .\KleineDatei04 + .\KleineDatei05 + .\KleineDatei06 + .\KleineDatei07 + .\KleineDatei08 + .\KleineDatei09 + .\KleineDatei10 + .\KleineDatei11 + .\KleineDatei12 + .\KleineDatei13 + .\KleineDatei14

Damit kann man dann ganz einfach

cmd.exe /C COPY /B KleineDatei01 + .\KleineDatei02 + .\KleineDatei03 + .\KleineDatei04 + .\KleineDatei05 + .\KleineDatei06 + .\KleineDatei07 + .\KleineDatei08 + .\KleineDatei09 + .\KleineDatei10 + .\KleineDatei11 + .\KleineDatei12 + .\KleineDatei13 + .\KleineDatei14 GroßeDatei.ZIP

Damit ist die Sache fast perfekt. Man kann noch eine Sicherung einbauen, um zu bestätigen, dass die Daten korrekt übertragen wurden, indem man einen Hash Wert der Daten berechnet. Damit wird gewährleistet, dass das Zusammenbauen der Blöcke auch in der korrekten Reihenfolge erfolgte.

Dazu verwendet man Get-FileHash, falls man Powershell 2.0 einsetzt, findet man hier eine Lösung: https://newyear2006.wordpress.com/2011/07/25/md5-und-sha1-datei-checksummen-mittels-powershell-errechnen-ein-ersatz-fr-fciv/.

Schreibe einen Kommentar

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s


%d Bloggern gefällt das: