Windows下使用powershell切割或合并文件

# Obtain a suitable buffer length by partial file length
function Get-BufferLength ([int]$partialFileLength)
{
    [int]$MinBufferLength = 1MB
    # No need to consume great amount memory,initialize as 50M, you can adjust it from here.
    [int]$MaxBufferLength = 50MB
 
    if($partialFileLength -ge 1GB) { return $MaxBufferLength}
    elseif( $partialFileLength -le 50MB) { return $MinBufferLength }
    else{ return [int]( $MaxBufferLength/1GB * $partialFileLength )}
}
 
# Write partial stream to file from current position
function Write-PartialStreamToFile
{
    param(
    [IO.FileStream]$stream,
    [long]$length,
    [string]$outputFile
    )
 
    #copy stream to file
    function Copy-Stream( [int]$bufferLength )
    {
        [byte[]]$buffer = New-Object byte[]( $bufferLength )
 
        # Read partial file data to memory buffer
        $stream.Read($buffer,0,$buffer.Length) | Out-Null
 
        # Flush buffer to file
        $outStream = New-Object IO.FileStream($outputFile,'Append','Write','Read')
        $outStream.Write($buffer,0,$buffer.Length)
        $outStream.Flush()
        $outStream.Close()
    }
 
    $maxBuffer = Get-BufferLength $length
    $remBuffer = 0
    $loop = [Math]::DivRem($length,$maxBuffer,[ref]$remBuffer)
 
    if($loop -eq 0)
    {
        Copy-Stream $remBuffer
        return
    }
 
    1..$loop | foreach {
        $bufferLength = $maxBuffer
 
        # let last loop contains remanent length
        if( ($_ -eq $loop) -and ($remBuffer -gt 0) ) 
        {
            $bufferLength = $maxBuffer + $remBuffer
         }
         Copy-Stream  $bufferLength
 
         # show outer progress
         $progress = [int]($_*100/$loop)
         write-progress -activity 'Writting file' -status 'Progress' -id 2 -percentcomplete $progress -currentOperation "$progress %"
    }
}
 
# split a large file into mutiple parts by part count or part length
function Split-File
{
    param(
    [Parameter(Mandatory=$True)]
    [IO.FileInfo]$File,
    [Switch]$ByPartCount,
    [Switch]$ByPartLength,
    [int]$PartCount,
    [int]$PartLength,
    [IO.DirectoryInfo]$OutputDir = '.'
    )
 
    # Argument validation
    if(-not $File.Exists) { throw "Source file [$File] not exists" }
    if(-not  $OutputDir.Exists) { mkdir $OutputDir.FullName | Out-Null}
    if( (-not $ByPartCount) -and (-not $ByPartLength) )
    {
        throw 'Must specify one of parameter, [ByPartCount] or [ByPartLength]'
    }
    elseif( $ByPartCount )
    {
        if($PartCount -le 1) {throw '[PartCount] must larger than 1'}
        $PartLength = $File.Length / $PartCount
    }
    elseif( $ByPartLength )
    {
        if($PartLength -lt 1) { throw '[PartLength] must larger than 0' }
        if($PartLength -ge $File.Length) { throw '[PartLength] must less than source file' }
        $temp = $File.Length /$PartLength
        $PartCount = [int]$temp
        if( ($File.Length % $PartLength) -gt 0 -and ( $PartCount -lt $temp ) )
        {
          $PartCount++
        }
    }
 
    $stream = New-Object IO.FileStream($File.FullName,
    [IO.FileMode]::Open ,[IO.FileAccess]::Read ,[IO.FileShare]::Read )
 
    # Make sure each part file name ended like '001' so that it's convenient to merge
    [string]$numberMaskStr = [string]::Empty.PadLeft( [int]([Math]::Log10($PartCount) + 1), "0" )
 
    1 .. $PartCount | foreach {
         $outputFile = Join-Path $OutputDir ( "{0}.part_{1} " -f $File.Name , $_.ToString( $numberMaskStr ) )
         # show outer progress
         $progress = [int]($_*100/$PartCount)
         write-progress -activity "Splitting file" -status "Progress $progress %" -Id 1 -percentcomplete $progress -currentOperation "Handle file $outputFile"
         if($_ -eq $PartCount)
         {
            Write-PartialStreamToFile $stream ($stream.Length - $stream.Position) $outputFile
         }
         else
         {
         Write-PartialStreamToFile $stream $PartLength  $outputFile
         }
    }
    $stream.Close()
}
 
function Merge-File
{
    param(
    [Parameter(Mandatory=$True)]
    [IO.DirectoryInfo]$SourceDir,
    [string]$Filter,
    [IO.FileInfo]$OutputFile
    )
 
    # arguments validation
    if ( -not $SourceDir.Exists ) { throw "Directory $SourceDir not exists." }
    $files = dir $SourceDir -File -Filter $Filter
    if($files -eq $null){ throw "No matched file in directory $SourceDir"}
 
    # output stream
    $outputStream = New-Object IO.FileStream($OutputFile.FullName,
        [IO.FileMode]::Append ,[IO.FileAccess]::Write ,[IO.FileShare]::Read )
 
    # merge file
    $files | foreach{
        #input stream
        $inputStream = New-Object IO.FileStream($_.FullName,
        [IO.FileMode]::Open ,[IO.FileAccess]::Read ,[IO.FileShare]::Read )
 
        $bufferLength = Get-BufferLength -partialFileLength $_.Length
        while($inputStream.Position -lt $inputStream.Length)
        {
            if( ($inputStream.Position + $bufferLength) -gt $inputStream.Length)
            {
                $bufferLength = $inputStream.Length - $inputStream.Position
            }
 
            # show outer progress
            $progress = [int]($inputStream.Position *100/ $inputStream.Length)
            write-progress -activity 'Merging file' -status "Progress $progress %"  -percentcomplete $progress
 
            # read file to memory buffer
            $buffer= New-Object byte[]( $bufferLength )
            $inputStream.Read( $buffer,0,$buffer.Length) | Out-Null
 
            #flush buffer to file
            $outputStream.Write( $buffer,0,$buffer.Length) | Out-Null
            $outputStream.Flush()
        }
        $inputStream.Close()
    }
    $outputStream.Close()}
使用示例
# 先导入 函数 Split-Merge-File
. 'E\Split-Merge-File.ps1' #注意句号和脚本之间有空格
 
# 将文件 ‘E:\win2012.vhdx’ 分割成20个小文件,输出至目录 'E:\VHD'
Split-File -File 'E:\win2012.vhdx' -ByPartCount -PartCount 20 -OutputDir 'E:\VHD'
 
# 将件‘E:\win2012.vhdx’按照每个大小 500MB 来分割,输出至目录 'E:\VHD'
Split-File -File 'E:\win2012.vhdx' -ByPartLength -PartLength 500MB -OutputDir 'E:\VHD'
 
# 将 'E:\VHD' 目录下包含 part 的所有文件合并,输出为 单个文件 'E:\win2012-2.vhdx'
Merge- File -SourceDir 'E:\VHD' -Filter "*part*" -OutputFile 'E:\win2012-2.vhdx'

 

posted @ 2019-09-12 11:18  e3tB8Wz7  阅读(2212)  评论(0编辑  收藏  举报