functions/Split-File.ps1

<#
.Synopsis
Splits a file to smaller files with choosable size.
.Description
Splits a file to smaller files with choosable size.
The source file remains unchanged.
The resulting files have the name of the source file with an appended forthcounting number.
.Parameter Path
Path of the source file
.Parameter NewSize
Size of the splitted parts (default is 100MB)
.Inputs
None
.Outputs
None
.Example
Split-File "BigFile.dat" 10000000
 
Divides the file BigFile.dat into parts of 10000000 byte size
.Notes
Author: Markus Scholtes
Version: 1.01
Date: 2020-05-03
#>

function Split-File([STRING] $Path, [INT64] $Newsize = 100MB)
{
    if ($Newsize -le 0)
    {
        Write-Error "Only positive sizes allowed"
        return
    }

    "Splitting file $Path to parts of $Newsize byte size"
    $FILEPATH = [IO.Path]::GetDirectoryName($Path)
    if ($FILEPATH -ne "") { $FILEPATH = $FILEPATH + "\" }
    $FILENAME = [IO.Path]::GetFileNameWithoutExtension($Path)
    $EXTENSION  = [IO.Path]::GetExtension($Path)

    $MAXVALUE = 1GB # Hard maximum limit for Byte array for 64-Bit .Net 4 = [INT32]::MaxValue - 56, see here https://stackoverflow.com/questions/3944320/maximum-length-of-byte
    # but only around 1.5 GB in 32-Bit environment! So I chose 1 GB just to be safe
    $PASSES = [MATH]::Floor($Newsize / $MAXVALUE)
    $REMAINDER = $Newsize % $MAXVALUE
    if ($PASSES -gt 0) { $BUFSIZE = $MAXVALUE } else { $BUFSIZE = $REMAINDER }

    $OBJREADER = New-Object System.IO.BinaryReader([System.IO.File]::Open($Path, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read))
    [Byte[]]$BUFFER = New-Object Byte[] $BUFSIZE
    $NUMFILE = 1

    do {
        $NEWNAME = "{0}{1}{2,2:00}{3}" -f ($FILEPATH, $FILENAME, $NUMFILE, $EXTENSION)

        $COUNT = 0
        $OBJWRITER = $NULL
        [INT32]$BYTESREAD = 0
        while (($COUNT -lt $PASSES) -and (($BYTESREAD = $OBJREADER.Read($BUFFER, 0, $BUFFER.Length)) -gt 0))
        {
            if (!$OBJWRITER)
            {
                $OBJWRITER = New-Object System.IO.BinaryWriter([System.IO.File]::Create($NEWNAME))
                "Writing to file $NEWNAME"
            }
            "Reading $BYTESREAD bytes of $Path"
            $OBJWRITER.Write($BUFFER, 0, $BYTESREAD)
            $COUNT++
        }
        if (($REMAINDER -gt 0) -and (($BYTESREAD = $OBJREADER.Read($BUFFER, 0, $REMAINDER)) -gt 0))
        {
            if (!$OBJWRITER)
            {
                $OBJWRITER = New-Object System.IO.BinaryWriter([System.IO.File]::Create($NEWNAME))
                "Writing to file $NEWNAME"
            }
            "Reading $BYTESREAD bytes of $Path"
            $OBJWRITER.Write($BUFFER, 0, $BYTESREAD)
        }

        if ($OBJWRITER) { $OBJWRITER.Close() }
        ++$NUMFILE
    } while ($BYTESREAD -gt 0)

    $OBJREADER.Close()
}


<#
.Synopsis
Joins files whose names or filesystem objects are handed in the pipeline to one target file
.Description
Joins files whose names or filesystem objects are handed in the pipeline to one target file.
If the target file exists it will be overwritten. The source files remain unchanged.
.Parameter Path
Path to the source file
.Inputs
Array of strings or filesystem objects
.Outputs
None
.Example
dir *.pdf | Join-File All.pdf
 
Joins all PDF files to target file All.pdf
.Example
"E.pdf", "C.pdf", "G.pdf", "V.pdf", "P.pdf"| Join-File .\Result.dat
 
Joins the listed PDF files to target file Result.dat
.Notes
Author: Markus Scholtes
Version: 1.0
Date: 2017-09-04
#>

function Join-File([STRING] $Path)
{
    if ((!$Path) -or ($Path -eq ""))
    {    Write-Error "Target filename missing."
        return
    }

    $OBJARRAY = @($INPUT)
    if ($OBJARRAY.Count -eq 0)
    {    Write-Error "Source filename list missing."
        return
    }

    $OBJWRITER = New-Object System.IO.BinaryWriter([System.IO.File]::Create($Path))

    $OBJARRAY | ForEach-Object {
        "Appending $_ to $Path."
        $OBJREADER = New-Object System.IO.BinaryReader([System.IO.File]::Open($_, [System.IO.FileMode]::Open, [System.IO.FileAccess]::Read))

        $OBJWRITER.BaseStream.Position = $OBJWRITER.BaseStream.Length
        $OBJREADER.BaseStream.CopyTo($OBJWRITER.BaseStream)
        $OBJWRITER.BaseStream.Flush()

        $OBJREADER.Close()
    }

    $OBJWRITER.Close()
}