Adam Adam - 7 months ago 43
Vb.net Question

Issue correctly calculating time remaining on file copy

I am having issue correctly estimating a realistic 'time remaining' app for a copy app. I am using a

Filestream
for the copy as the files being copied vary from >1MB to 4GB. I am using
percentageTotal
here to calculate the current position of the filecopy over all:

'Loop through each file in the SourceDir
For Each ChildFile In SourceDir.GetFiles()

If (worker.CancellationPending = True) Then
e.Cancel = True
End If

'Calculate data being moved for eta to completion
Dim filetotalbytes As Double = ChildFile.Length
filetotalsofarcopied = filetotalbytes + filetotalsofarcopied


'Display file being copied
SetLabelText_ThreadSafe(Me.lblStatus, "Copying: " & line & "\" & ChildFile.Name & "")

'Do the copy
ChildFile.CopyTo(Path.Combine(DestDir.FullName, ChildFile.Name), True)

'Contruct Destination and Source Strings
deststring = DestDir.ToString & "\" & ChildFile.Name
Dim sourcedirstring As String
sourcedirstring = SourceDir.ToString & "\" & ChildFile.Name

Dim CopyStream As New FileStream(sourcedirstring, FileMode.Open, FileAccess.Read)
Dim NewStream As New FileStream(deststring, FileMode.Append)

Dim Buffer(4096) As Byte
Dim BytesRead As Integer
Dim len As Long = CopyStream.Length - 1

While CopyStream.Position < len


BytesRead = CopyStream.Read(Buffer, 0, Buffer.Length)
NewStream.Write(Buffer, 0, BytesRead)

percentageTotal = ((NewStream.Length + filetotalsofarcopied) / Overallsize * 100)
percentageTotal = Decimal.Round(percentageTotal)

' SetLabelText_ThreadSafe(Me.lblTotalProgress, "" & percentageTotal & "%")

End While

CopyStream.Dispose()
NewStream.Dispose()


I find that the value of
percentageTotal
often jumps rather than increases in a liner fashion as I would expect. Can anyone see where I am going wrong here?

Additionally I cannot perfect the logic of the
secondsremaining
value. It currently shows the copy taking days when initially run before quickly decreasing but obviously not the correct time remaining.

'Start process snippit
'Start Button Click config - starts the backgroundworker
Public Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

'Set start value - label22 is hidden on form1
Label22.Text = "0"

'Start timers
Timer3.Start()
Timer4.Start()


'Start background worker
BackgroundWorker1.WorkerSupportsCancellation = True
BackgroundWorker1.WorkerReportsProgress = True
BackgroundWorker1.RunWorkerAsync()

End Sub


UPDATED! And the Timer.Tick code at the end of the form calculating the time remaining:

Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
byteslastsecond = filetotalsofarcopied
End Sub


'Every tick of Timer4 calculates the time needed to complete the copy.
Private Sub Timer4_Tick(sender As Object, e As EventArgs) Handles Timer4.Tick
Dim time As Long = Label22.Text
If percentageTotal = 0 Then
SetLabelText_ThreadSafe(Me.lblEstTimeLeft, "Estimated Time Left (All Files): Estimating...")
Else
Dim secondsRemaining As Double = (Overallsize - filetotalsofarcopied) / (filetotalsofarcopied - byteslastsecond)
Dim ts As TimeSpan = TimeSpan.FromSeconds(secondsRemaining)


Can anyone correct my logic for the time remaining to be more realistic?

Answer

The problem with your code is that you increment filetotalsofarcopied by the entire file size even though the entire file isn't copied yet. You should increment it as you copy a block of data.

So remove this:

'Calculate data being moved for eta to completion
Dim filetotalbytes As Double = ChildFile.Length
filetotalsofarcopied = filetotalbytes + filetotalsofarcopied

and put it in your While-loop instead:

BytesRead = CopyStream.Read(Buffer, 0, Buffer.Length)
NewStream.Write(Buffer, 0, BytesRead)

filetotalsofarcopied += BytesRead

percentageTotal = ((NewStream.Length + filetotalsofarcopied) / Overallsize * 100)
percentageTotal = Decimal.Round(percentageTotal)

This should make both the time estimation work correctly, and the percentage display.


You should also remove this:

'Do the copy
ChildFile.CopyTo(Path.Combine(DestDir.FullName, ChildFile.Name), True)

because you're wasting both time and resources by copying the file twice.