Split-File.ps1
function Split-File { <# .SYNOPSIS Splits a file into multiple parts .DESCRIPTION Splits a file into smaller parts. The maximum size of the part files can be specified. The number of parts required is calculated. .EXAMPLE Split-File -Path 'c:\test.zip' -PartSizeBytes 2.5MB Splits the file c:\test.zip in as many parts as necessary. Each part file is no larger than 2.5MB .EXAMPLE Split-File -Path 'c:\test.zip' -PartSizeBytes 2.5MB -AddSelfExtractor Splits the file c:\test.zip in as many parts as necessary. Each part file is no larger than 2.5MB Adds a powershell script that joins the parts when run, and adds a shortcut file to run the PowerShell extractor script on double-click, essentially adding a self-extractor #> param ( # Path to the file you want to split [Parameter(Mandatory,HelpMessage='Path to the file you want to split')] [String] $Path, # maximum size of file chunks (in bytes) [int] $PartSizeBytes = 1MB, # when specified, add a an extractor script and link file to easily convert # chunks back into the original file [Switch] $AddSelfExtractor ) try { # get the path parts to construct the individual part # file names: $fullBaseName = [IO.Path]::GetFileName($Path) $baseName = [IO.Path]::GetFileNameWithoutExtension($Path) $parentFolder = [IO.Path]::GetDirectoryName($Path) $extension = [IO.Path]::GetExtension($Path) # get the original file size and calculate the # number of required parts: $originalFile = New-Object -TypeName System.IO.FileInfo -ArgumentList ($Path) $totalChunks = [int]($originalFile.Length / $PartSizeBytes) + 1 $digitCount = [int][Math]::Log10($totalChunks) + 1 # read the original file and split into chunks: $reader = [IO.File]::OpenRead($Path) $count = 0 $buffer = New-Object -TypeName Byte[] -ArgumentList $PartSizeBytes $moreData = $true # read chunks until there is no more data while($moreData) { # read a chunk $bytesRead = $reader.Read($buffer, 0, $buffer.Length) # create the filename for the chunk file $chunkFileName = "$parentFolder\$fullBaseName.{0:D$digitCount}.part" -f $count Write-Verbose -Message "saving to $chunkFileName..." $output = $buffer # did we read less than the expected bytes? if ($bytesRead -ne $buffer.Length) { # yes, so there is no more data $moreData = $false # shrink the output array to the number of bytes # actually read: $output = New-Object -TypeName Byte[] -ArgumentList $bytesRead [Array]::Copy($buffer, $output, $bytesRead) } # save the read bytes in a new part file [IO.File]::WriteAllBytes($chunkFileName, $output) # increment the part counter ++$count } # done, close reader $reader.Close() # add self-extractor if ($AddSelfExtractor) { Write-Verbose -Message "Adding extractor scripts..." # define the self-extractor powershell script: $extractorName = "${fullBaseName}.{0:D$digitCount}.part.ps1" -f $count $extractorPath = Join-Path -Path $parentFolder -ChildPath $extractorName $filePath = '$PSScriptRoot\' + "$baseName$extension" # define the self-extractor shortcut file that launches # the powershell script on double-click: $linkName = "Extract ${fullBaseName}.lnk" $linkPath = Join-Path -Path $parentFolder -ChildPath $linkName # this will be used inside the extractor script to find the # part files via relative path: $currentFile = '"$PSCommandPath"' $currentFolder = '"$PSScriptRoot"' # write the extractor script source code to file: " # copy the join-file source code into the extractor script: function Join-File { ${function:Join-File} } # join the part files and delete the part files after joining: Join-File -Path ""$filePath"" -Verbose -DeletePartFiles # remove both extractor scripts: (Join-Path -Path $currentFolder -ChildPath '$linkName') | Remove-Item Remove-Item -Path $currentFile # open the extracted file in windows explorer explorer.exe ""/select,""""$filepath"""""" " | Set-Content -Path $extractorPath # create a shortcut file that launches the extractor script # when it is double-clicked: $shell = New-Object -ComObject WScript.Shell $scut = $shell.CreateShortcut($linkPath) $scut.TargetPath = "powershell.exe" $scut.Arguments = "-nop -executionpolicy bypass -file ""$extractorPath""" $scut.WorkingDirectory = "" $scut.IconLocation = "$env:windir\system32\shell32.dll,162" $scut.Save() } } catch { throw "Unable to split file ${Path}: $_" } } |