public/Split-Csv.ps1

using namespace System.Text
using namespace System.IO

function Split-Csv {
    [CmdletBinding(DefaultParameterSetName = 'ByChunks')]
    param(
        [Parameter(Position = 0, Mandatory, ValueFromPipeline, ValueFromPipelineByPropertyName)]
        [alias('FullName')]
        [string] $Path,

        [Parameter(Mandatory)]
        [string] $DestinationFolder,

        [Parameter(ParameterSetName = 'BySize')]
        [int64] $Size = 1kb,

        [Parameter(ParameterSetName = 'ByChunks')]
        [int32] $Chunks = 3,

        [Parameter()]
        [EncodingTransformation()]
        [ArgumentCompleter([EncodingCompleter])]
        [Encoding] $Encoding = 'utf8',

        [Parameter()]
        [switch] $PassThru
    )

    begin {
        $Destination = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($DestinationFolder)

        class ChunkWriter {
            [FileInfo] $Source
            [string] $Destination
            [string] $Headers
            [string] $Format
            [Encoding] $Encoding

            [StreamWriter] GetNewWriter([int32] $Index) {
                $name     = [string]::Format(
                    '{0} - Part {1}{2}',
                    $this.Source.BaseName,
                    $Index.ToString($this.Format),
                    $this.Source.Extension
                )
                $newChunk = Join-Path $this.Destination -ChildPath $name
                $writer   = [StreamWriter]::new($newChunk, $false, $this.Encoding)
                $writer.AutoFlush = $true
                $writer.WriteLine($this.Headers)
                return $writer
            }
        }
    }
    process {
        try {
            [FileInfo] $Path = $PSCmdlet.GetUnresolvedProviderPathFromPSPath($Path)
            $null    = [Directory]::CreateDirectory($Destination)
            $reader  = [StreamReader]::new($Path.FullName, $Encoding, $true)
            $headers = $reader.ReadLine()
            $Index   = 0

            if($PSCmdlet.ParameterSetName -eq 'ByChunks') {
                $chunkSize = ($Path.Length - $headers.Length) / $Chunks + ($headers.Length * $Chunks)
                $format    = 'D{0}' -f $Chunks.ToString().Length
            }
            else {
                $chunkSize = $Size - $headers.Length
                $format    = 'D{0}' -f [math]::Ceiling($Path.Length / $Size).ToString().Length
            }

            $chunkWriter = [ChunkWriter]@{
                Source      = $Path
                Destination = $Destination
                Headers     = $headers
                Format      = $format
                Encoding    = $Encoding
            }

            $writer = $chunkWriter.GetNewWriter($Index++)

            while(-not $reader.EndOfStream) {
                if($writer.BaseStream.Length -ge $chunkSize) {
                    $writer.Dispose()

                    if($PassThru.IsPresent) {
                        $writer.BaseStream.Name -as [FileInfo]
                    }

                    $writer = $chunkWriter.GetNewWriter($Index++)
                }
                $writer.WriteLine($reader.ReadLine())
            }
        }
        catch {
            $PSCmdlet.ThrowTerminatingError($_)
        }
        finally {
            $writer, $reader | ForEach-Object Dispose

            if($PassThru.IsPresent) {
                $writer.BaseStream.Name -as [FileInfo]
            }
        }
    }
}