修复此方法中的进度报告事件

时间:2014-08-14 12:43:32

标签: .net vb.net math byte progress

我试过编写一个将文件拆分成较小部分的方法(我将这些部分称为“块”),我使用1 MB的缓冲区来读取/写入块,然后我报告操作进度百分比和当前的块进度百分比。

问题是这个,我有一个6,74 GB的大文件,我将它分成每个1 GB的块,进度报告按预期工作但不是最后一个块,其中进度仅为75 %'因为当然最后一部分大约是750 mb而不是1 GB,那么进度只会在最后一个块中显示高达75%。

这就是我获取百分比的方法,错误的进度是ChunkProgress块中的Else值:

If Not ChunkIndex = ChunkCount Then
    ProgressArguments =
        New SplitProgressChangedArgs(
            TotalProgress:=(TotalSize - SizeRemaining) * (100I / TotalSize),
            ChunkProgress:=(100I / ChunkSize) * (SizeWritten - BufferLength),
            ChunksToCreate:=ChunkCount + 1,
            ChunksCreated:=ChunkIndex)

Else
    ProgressArguments =
        New SplitProgressChangedArgs(
            TotalProgress:=(TotalSize - SizeRemaining) * (100I / TotalSize) - 1.0R,
            ChunkProgress:=(100I / ChunkSize) * (SizeWritten - InputStream.Length),
            ChunksToCreate:=ChunkCount + 1,
            ChunksCreated:=ChunkIndex)

End If
  

TotalSize =要拆分的文件的固定大小,以字节为单位(在本例中为6,74 gb)

     

ChunkSize =块的固定大小,以字节为单位(在本例中为1 GB)

     

SizeRemaining =总剩余大小计数器,以字节为单位(在本例中为6,74 gb到0)。

     

SizeWritten =写入计数器的块大小,以字节为单位(在本例中为0到1 GB)

在写最后一块时,有人可以帮我修正百分比吗?

这是Class的相关代码:

''' <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>
''' Splits the specified file.
''' </summary>
''' <param name="InputFile">Indicates the file to split.</param>
''' <param name="ChunkSize">Indicates the size of each chunk.</param>
''' <param name="ChunkName">Indicates the name-format for the chunks.</param>
''' <param name="ChunkExt">Indicates the file-extension for the chunks.</param>
''' <param name="Overwrite">
''' If set to <c>true</c> any existing file will be overwritten if needed to create a chunk, 
''' otherwise, an exception will be thrown.
''' </param>
''' <param name="DeleteAfterSplit">If set to <c>true</c> the input file will be deleted after a successful split.</param>
''' <exception cref="System.OverflowException">'ChunkSize' should be smaller than the Filesize.</exception>
''' <exception cref="System.IO.IOException">File already exist</exception>
Public Sub Split(ByVal InputFile As String,
                 ByVal ChunkSize As Long,
                 Optional ByVal ChunkName As String = Nothing,
                 Optional ByVal ChunkExt As String = Nothing,
                 Optional ByVal Overwrite As Boolean = False,
                 Optional ByVal DeleteAfterSplit As Boolean = False)

    ' The progress event arguments.
    Dim ProgressArguments As SplitProgressChangedArgs

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

    ' The total filesize to split, in bytes.
    Dim TotalSize As Long = fInfo.Length

    ' 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 write the chunks.
    Dim Buffer As Byte() = New Byte() {}

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

    ' The total amount of chunks to create.
    Dim ChunkCount As Integer = CInt(Math.Floor(fInfo.Length / ChunkSize))

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

    ' A zero-filled string to enumerate the chunk files.
    Dim Zeros As String = String.Empty

    ' The given filename for each chunk.
    Dim ChunkFile As String = String.Empty

    ' The chunk file basename.
    ChunkName = If(String.IsNullOrEmpty(ChunkName),
                   Path.Combine(fInfo.DirectoryName, Path.GetFileNameWithoutExtension(fInfo.Name)),
                   Path.Combine(fInfo.DirectoryName, ChunkName))

    ' The chunk file extension.
    ChunkExt = If(String.IsNullOrEmpty(ChunkExt),
                  fInfo.Extension.Substring(1I),
                  ChunkExt)

    ' If ChunkSize is bigger than filesize then...
    If ChunkSize >= fInfo.Length Then
        Throw New OverflowException("'ChunkSize' should be smaller than the Filesize.")
        Exit Sub

        ' For cases where a chunksize is smaller than the buffersize.
    ElseIf ChunkSize < BufferLength Then
        BufferLength = CInt(ChunkSize)

    End If ' ChunkSize <>...

    ' If not file-overwrite is allowed then...
    If Not Overwrite Then

        For Index As Integer = 0I To (ChunkCount)

            ' Set chunk filename.
            Zeros = New String("0", CStr(ChunkCount).Length - CStr(Index + 1I).Length)
            ChunkFile = String.Format("{0}.{1}.{2}", ChunkName, Zeros & CStr(Index + 1I), ChunkExt)

            ' If chunk file already exists then...
            If File.Exists(ChunkFile) Then

                Throw New IOException(String.Format("File already exist: {0}", ChunkFile))
                Exit Sub

            End If ' File.Exists(ChunkFile)

        Next Index

        Zeros = String.Empty
        ChunkFile = String.Empty

    End If ' Overwrite

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

        Using BinaryReader As New BinaryReader(InputStream)

            While (InputStream.Position < InputStream.Length)

                ' Set chunk filename.
                Zeros = New String("0", CStr(ChunkCount).Length - CStr(ChunkIndex + 1I).Length)
                ChunkFile = String.Format("{0}.{1}.{2}", ChunkName, Zeros & CStr(ChunkIndex + 1I), ChunkExt)

                ' Reset written byte-length counter.
                SizeWritten = 0L

                ' Create the chunk file to Write the bytes.
                Using OutputStream As New FileStream(ChunkFile, FileMode.Create)

                    Using BinaryWriter As New BinaryWriter(OutputStream)

                        ' Read until reached the end-bytes of the input file.
                        While (SizeWritten < ChunkSize) AndAlso (InputStream.Position < InputStream.Length)

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

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

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

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

                            If Not ChunkIndex = ChunkCount Then
                                ProgressArguments =
                                    New SplitProgressChangedArgs(
                                        TotalProgress:=(TotalSize - SizeRemaining) * (100I / TotalSize),
                                        ChunkProgress:=(100I / ChunkSize) * (SizeWritten - BufferLength),
                                        ChunksToCreate:=ChunkCount + 1,
                                        ChunksCreated:=ChunkIndex)

                            Else
                                ProgressArguments =
                                    New SplitProgressChangedArgs(
                                        TotalProgress:=(TotalSize - SizeRemaining) * (100I / TotalSize) - 1.0R,
                                        ChunkProgress:=(100I / ChunkSize) * (SizeWritten - InputStream.Length),
                                        ChunksToCreate:=ChunkCount + 1,
                                        ChunksCreated:=ChunkIndex)

                            End If

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

                        End While ' (SizeWritten < ChunkSize) AndAlso (InputStream.Position < InputStream.Length)

                        OutputStream.Flush()

                    End Using ' BinaryWriter

                End Using ' OutputStream

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

            End While ' InputStream.Position < InputStream.Length

        End Using ' BinaryReader

    End Using ' InputStream

End Sub

这是一个示例用法:

Splitter.Split(InputFile:="C:\Test.mkv",
               ChunkSize:=1073741824L,
               ChunkName:="Test.Part",
               ChunkExt:="mp3",
               Overwrite:=True,
               DeleteAfterSplit:=False)

1 个答案:

答案 0 :(得分:1)

进度没有在最后一个块上正确显示的原因是因为你正在计算正常&#34;正常&#34;块大小

例如(简化值):

500 out of 1000 bytes with ChunkSize = 1000
500 / ChunkSize = %
500/1000 = 0.5

如果最后一个块只有750个字节,我们必须通过除(thisChunk / ChunkSize)来纠正:

500 out of 750 bytes with ChunkSize = 1000
500 / ChunkSize / (thisChunk / ChunkSize) = %
500/1000 / (750/1000) = 0.667

请针对Else案例进行尝试。 (InputStream.Length - SizeWritten)应该获得剩余块的大小:

ProgressArguments =
  New SplitProgressChangedArgs(
  TotalProgress:=(TotalSize - SizeRemaining) * (100I / TotalSize) - 1.0R,
  ChunkProgress:=(100I / ChunkSize) * (SizeWritten - InputStream.Length) / ((InputStream.Length - SizeWritten) / ChunkSize),
  ChunksToCreate:=ChunkCount + 1,
  ChunksCreated:=ChunkIndex)