ElektroStudios
ElektroStudios

Reputation: 20464

Fix progress report in this method that merge files

I've written a method that splits a file into little chunks (here) and this is the method that merges those splitted chunks.

The problem I have is with the progress report, the calculation of the ChunkProgress value is wrong:

enter image description here

It should go from 0% to 100% per each chunk.

How I can fix it?

This is how I'm getting the value:

ChunkProgress = (100I / InputStream.Length) * (SizeWritten - BufferLength)

InputStream.Length = The size in bytes of the current chunk.

SizeWritten = The total amount of written bytes from the first chunk.

BufferLength= The fixed buffer size used to read/write bytes (in this case is 1 megabyte)

This is the relevant code:

''' <summary>
''' Gets or sets the buffer-size to split or merge, in Bytes.
''' Default value is: 1048576 bytes (1 megabyte).
''' </summary>
''' <value>The buffer-size.</value>
Public Property BufferSize As Integer = 1048576I


''' <summary>
''' Merges the specified file.
''' </summary>
''' <param name="InputFile">
''' Indicates the file to merge its chunks.
''' This should be the first chunk file (eg: 'File.Part.01.mkv')
''' </param>
''' <param name="OutputFile">Indicates the output file.</param>
''' <param name="Overwrite">
''' If set to <c>true</c> the chunks will be deleted after a successful merge, 
''' otherwise, an exception will be thrown.
''' </param>
''' <exception cref="System.IO.IOException">File already exist</exception>
Public Sub Merge(ByVal InputFile As String,
                 Optional ByVal OutputFile As String = Nothing,
                 Optional ByVal Overwrite As Boolean = False,
                 Optional DeleteChunksAfterMerged As Boolean = False)

    If Not File.Exists(InputFile) Then
        Throw New FileNotFoundException("The specified file doesn't exists.", InputFile)
        Exit Sub

    ElseIf Not Overwrite AndAlso File.Exists(OutputFile) Then
        Throw New IOException(String.Format("File already exist: {0}", OutputFile))
        Exit Sub

    End If

    ' The progress event arguments.
    Dim ProgressArguments As MergeProgressChangedArgs

    ' FileInfo instance of the input chunk file.
    Dim fInfo As New FileInfo(InputFile)

    ' Get the filename without extension.
    Dim Filename As String = Path.GetFileNameWithoutExtension(fInfo.FullName)
    ' Remove the chunk enumeration from the filename.
    Filename = Filename.Substring(0I, Filename.LastIndexOf("."c))

    ' TSet the pattern to find the chunk files to merge.
    Dim ChunkPatternSearch As String =
        Filename & ".*" & If(Not String.IsNullOrEmpty(fInfo.Extension), fInfo.Extension, "")

    ' Retrieve all the splitted files to merge them.
    Dim Chunks As IEnumerable(Of FileInfo) =
       From Chunk As String In
       Directory.GetFiles(fInfo.DirectoryName, ChunkPatternSearch, SearchOption.TopDirectoryOnly)
       Select New FileInfo(Chunk)

    ' The total filesize to merge, in bytes.
    Dim TotalSize As Long =
        (From Chunk As FileInfo In Chunks Select Chunk.Length).Sum

    ' The remaining size to calculate the percentage, in bytes.
    Dim SizeRemaining As Long = TotalSize

    ' Counts the length of the current chunk file to calculate the percentage, in bytes.
    Dim SizeWritten As Long = 0L

    ' The buffer to read data and merge the chunks.
    Dim Buffer As Byte() = New Byte() {}

    ' The buffer length.
    Dim BufferLength As Integer = Me.BufferSize

    ' The total amount of chunks to merge.
    Dim ChunkCount As Integer = Chunks.Count

    ' Keeps track of the current chunk.
    Dim ChunkIndex As Integer = 0I

    ' Keeps track of the total percentage done.
    Dim TotalProgress As Double = 0.0R

    ' Keeps track of the current chunk percentage done.
    Dim ChunkProgress As Double = 0.0R

    ' Create the output file to merge the chunks inside.
    Using OutputStream As New FileStream(OutputFile, FileMode.Create)

        Using BinaryWriter As New BinaryWriter(OutputStream)

            ' Iterate the chunks.
            For Each Chunk As FileInfo In Chunks

                ' Open the chunk to start reading bytes.
                Using InputStream As New FileStream(Chunk.FullName, FileMode.Open)

                    Using BinaryReader As New BinaryReader(InputStream)

                        ' Read until reached the end-bytes of the chunk file.
                        While (InputStream.Position < InputStream.Length)

                            ' Read bytes from the chunk file (BufferSize byte-length).
                            Buffer = BinaryReader.ReadBytes(BufferLength)

                            ' Write those bytes in the output file.
                            BinaryWriter.Write(Buffer)

                            ' Increment the bytes-written counter.
                            SizeWritten += Buffer.Count

                            ' Decrease the bytes-remaining counter.
                            SizeRemaining -= Buffer.Count

                            TotalProgress = (TotalSize - SizeRemaining) * (100I / TotalSize)
                            ChunkProgress = (100I / InputStream.Length) * (SizeWritten - BufferLength)
                            ' (100I / InputStream.Length) * (SizeWritten - BufferLength)

                            ' Set the progress event-arguments.
                            ProgressArguments = New MergeProgressChangedArgs(
                                TotalProgress:=TotalProgress,
                                ChunkProgress:=ChunkProgress,
                                ChunksToMerge:=ChunkCount,
                                ChunksMerged:=ChunkIndex)

                            ' Report the progress.
                            RaiseEvent MergeProgressChanged(Me, ProgressArguments)

                        End While ' (InputStream.Position < InputStream.Length)

                        ChunkIndex += 1I 'Increment the chunk file counter.

                    End Using ' BinaryReader

                End Using ' InputStream

            Next Chunk

            OutputStream.Flush()

        End Using ' BinaryWriter

    End Using ' OutputStream

    If DeleteChunksAfterMerged Then ' Delethe the chunk files.

        For Each Chunk As FileInfo In Chunks
            File.Delete(Chunk.FullName)
        Next Chunk

    End If ' DeleteChunksAfterMerged

End Sub

Upvotes: 0

Views: 43

Answers (1)

You need this to calc the percentage for the entire file:

' Increment the bytes-written counter.
   SizeWritten += Buffer.Count

But it doesnt have any role in reporting the chunk progress. If you look at the GIF, you'll see it tips over to 100% right at the moment it starts writing chunk #2, then again to 200% when you start to do Chunk 3. To fix it, you need a new ChunkBytesWritten type var:

' Increment the bytes-written counter.
 SizeWritten += Buffer.Count
 ChunkBytes+= Buffer.Count

Then reset it when the ChunkIndex changes:

ChunkIndex += 1
ChunkBytes = 0

The calc then should be:

ChunkProgress = (100I / InputStream.Length) * (ChunkBytes - BufferLength)

Upvotes: 2

Related Questions