I am wanting the most efficient way to copy a file with a progress bar updating the copy percentage.
This is the standard copy code I am using:
System.IO.File.Copy(source,target)
This is very fast and efficient. However, I cannot report the copy percentage.
I have tried many functions that read and save a file by opening up the filestream and then looping while reading/writing the data. This enables me to report the progress.
Here are the functions that I am using:
Public Sub SaveBinaryFile(strFilename As String, bytesToWrite() As Byte)
Dim position As Integer = 0
Dim BufferSize As Integer = 4096
'frmMain.tsProgressBar.Value = 0
Using fsNew As FileStream = New FileStream(strFilename, FileMode.Create, FileAccess.Write)
Do
Dim intToCopy As Integer = Math.Min(BufferSize, bytesToWrite.Length - position)
Dim buffer(intToCopy - 1) As Byte
Array.Copy(bytesToWrite, position, buffer, 0, intToCopy)
fsNew.Write(buffer, 0, buffer.Length)
'frmMain.tsProgressBar.Value = ((position / bytesToWrite.Length) * 100)
'frmMain.tsProgressBar.Refresh()
Application.DoEvents()
position += intToCopy
Loop While position < bytesToWrite.Length
End Using
End Sub
Public Function ReadBinaryFile(strFilename As String) As Byte()
Dim position As Integer = 0
Dim bufferSize As Integer = 4096
Dim bytes() As Byte
'frmMain.tsProgressBar.Value = 0
Using fsOpen As FileStream = New FileStream(strFilename, FileMode.Open)
ReDim bytes((fsOpen.Length) - 1)
Do
If (position + bufferSize) > fsOpen.Length Then
fsOpen.Read(bytes, position, fsOpen.Length - position)
Exit Do
Else
fsOpen.Read(bytes, position, bufferSize)
End If
'frmMain.tsProgressBar.Value = ((position / fsOpen.Length) * 100)
'frmMain.tsProgressBar.Refresh()
Application.DoEvents()
position += bufferSize
Loop
End Using
Return bytes
End Function
The problem is that is a lot slower than using the straight copy code.
What is the best/efficient way to copy a file showing the copy progress?
thanks
There is a variant of System.IO.File that provides user feedback; it's called Microsoft.VisualBasic.FileIO.FileSystem. See also http://msdn.microsoft.com/en-us/library/cc165446.aspx.
hmm Try this function
Dim D As Integer
Function CopyFileWithProgress(ByVal Source As String, ByVal Destination As String) As Integer
Try
Dim SourceF As New IO.FileStream(TextBox1.Text, IO.FileMode.Open)
Dim DestinationF As New IO.FileStream(TextBox2.Text & "\" & TextBox3.Text & ".ps4game", IO.FileMode.Create)
Dim len As Long = SourceF.Length - 1
Dim buffer(1024) As Byte
Dim byteCFead As Integer
While SourceF.Position < len
byteCFead = (SourceF.Read(buffer, 0, 1024))
DestinationF.Write(buffer, 0, byteCFead)
D = CInt(SourceF.Position / len * 100)
Application.DoEvents()
End While
DestinationF.Flush()
DestinationF.Close()
SourceF.Close()
Return D
Catch ex As Exception
MsgBox(ex.Message)
End Try
End Function
it will work
you can use:
Microsoft.VisualBasic.FileIO.FileSystem.CopyFile("C:\[Copy From]", "C:\[Copy To]", _
FileIO.UIOption.AllDialogs)
' this requires the Microsoft.VisualBasic.dll, which is referenced from Visual Basic .NET
' projects by default, but needs to be added to C# projects manually
OR :
Dim CF As New IO.FileStream("C:\[Copy From]", IO.FileMode.Open)
Dim CT As New IO.FileStream("C:\[Copy To]", IO.FileMode.Create)
Dim len As Long = CF.Length - 1
Dim buffer(1024) As Byte
Dim byteCFead As Integer
While CF.Position < len
byteCFead = (CF.Read(buffer, 0, 1024))
CT.Write(buffer, 0, byteCFead)
ProgressBar1.Value = CInt(CF.Position / len * 100)
Application.DoEvents()
End While
CT.Flush()
CT.Close()
CF.Close()
OR : Using WebClient
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If Not OpenFileDialog1.ShowDialog() = DialogResult.OK Then Return
If Not FolderBrowserDialog1.ShowDialog = DialogResult.OK Then Return
Button1.Enabled = False
WebClient1.DownloadFileAsync(New Uri(OpenFileDialog1.FileName), Path.Combine(FolderBrowserDialog1.SelectedPath, OpenFileDialog1.SafeFileName))
End Sub
Private Sub WebClient1_DownloadProgressChanged(sender As Object, e As DownloadProgressChangedEventArgs) Handles WebClient1.DownloadProgressChanged
ProgressBar1.Value = e.ProgressPercentage
End Sub
Private Sub WebClient1_DownloadFileCompleted(sender As Object, e As System.ComponentModel.AsyncCompletedEventArgs) Handles WebClient1.DownloadFileCompleted
Button1.Enabled = True
End Sub
Related
i have a problem printing an large image over multiple Pages. I have scanned A4 Documents with 1-x Pages and they put together as one long image. I have to rescale Image to fit in width on A4 and to print with as many pages as on image are. There ist my code, can't achieve this, printing only first page. Any help woud be aprechiated. Thanks! (i'm a beginner...)
Imports System.Drawing.Printing
Imports System.Drawing.Imaging
Imports System.IO
Public Class Form1
Dim Bmp As Bitmap
dim pageNum As Int32 = 0
Private Sub PrintDocument4_PrintPage(ByVal sender As Object, ByVal e As PrintPageEventArgs) Handles PrintDocument4.PrintPage
Dim WidthRatio As Double = e.MarginBounds.Width / Bmp.Width
Dim maxpagenum As Int32 = Math.Ceiling(Bmp.Height / e.MarginBounds.Height * WidthRatio)
Debug.WriteLine(maxpagenum)
Debug.WriteLine(e.MarginBounds.Height)
Dim piece As New Bitmap(CInt(e.MarginBounds.Width / WidthRatio), CInt(e.MarginBounds.Height / WidthRatio))
Dim dest_rect As New Rectangle(0, 0, CInt(e.MarginBounds.Width / WidthRatio), CInt(e.MarginBounds.Height / WidthRatio))
Using gr As Graphics = Graphics.FromImage(piece)
Dim source_rect As New Rectangle(0, 0, CInt(e.MarginBounds.Width / WidthRatio), CInt(e.MarginBounds.Height / WidthRatio))
For i As Integer = 1 To maxpagenum
source_rect.X = 0
' Copy the piece of the image.
gr.DrawImage(Bmp, dest_rect, source_rect, _
GraphicsUnit.Pixel)
'piece.Save("Bmp" & i & ".jpg", ImageFormat.Bmp)
e.Graphics.DrawImage(piece, 0, source_rect.Y, CInt(piece.Width * WidthRatio), CInt(piece.Height * WidthRatio))
pageNum += 1
source_rect.Y += CInt(e.MarginBounds.Height / WidthRatio)
If pageNum < maxpagenum Then
e.HasMorePages = True
Else
pageNum = 0
End If
Next
End Using
End Sub
Private Sub Button9_Click(sender As Object, e As EventArgs) Handles Button9.Click
If ListBox1.SelectedItem IsNot Nothing Then
Dim selItem As String = ListBox1.SelectedItem.ToString()
Dim fs1 As System.IO.FileStream
fs1 = New System.IO.FileStream(selItem, IO.FileMode.Open, IO.FileAccess.Read)
Bmp = New Bitmap(System.Drawing.Image.FromStream(fs1))
fs1.Close()
PrintDocument4 = New PrintDocument
With PrintDialog1
.AllowCurrentPage = False
.AllowPrintToFile = False
.AllowSelection = False
.AllowSomePages = False
.Document = PrintDocument4
.PrinterSettings.DefaultPageSettings.Margins.Top = 15
.PrinterSettings.DefaultPageSettings.Margins.Bottom = 15
.PrinterSettings.DefaultPageSettings.Margins.Left = 15
.PrinterSettings.DefaultPageSettings.Margins.Right = 15
End With
If PrintDialog1.ShowDialog = DialogResult.OK Then
PrintDocument4.PrinterSettings = PrintDialog1.PrinterSettings
AddHandler PrintDocument4.PrintPage, AddressOf PrintDocument4_PrintPage
PrintDocument4.Print()
End If
End If
End Sub
I build an app, which searches for different files through the computer, including Windows, ProgramFiles, etc folders.
I've already done with the 'recursive' file search and it works now, this means all files/folders which are inaccessible will be skipped and the software will move on with the other available folders/files. But now, I have other issues...
The problem is that, for some odd reason, the application does not correctly report the progressbar value accordingly to the progress made in searching the files. This means that, the file search continues, BUT the progressbar is already 100%. The progressbar value SHOULD be 100% when the file search has been successfully completed.
====other code is already here ====
Dim int As Integer = 0
Dim filecount As Integer = 0
Private Function GetFilesRecursive(ByVal path As String) As List(Of String)
Dim lstResult As New List(Of String)
Dim stkStack As New Stack(Of String)
stkStack.Push(path)
Do While (stkStack.Count > 0)
Dim strDirectory As String = stkStack.Pop
Try
lstResult.AddRange(Directory.GetFiles(strDirectory, "*.*"))
Dim strDirectoryName As String
For Each strDirectoryName In Directory.GetDirectories(strDirectory)
stkStack.Push(strDirectoryName)
Next
Catch ex As Exception
End Try
Loop
Return lstResult
End Function
Private Sub BackgroundWorker1_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles Quickscan.DoWork
Try
Dim i As Integer
Dim files As List(Of String) = GetFilesRecursive(FolderBrowserDialog1.SelectedPath)
For Each file As String In files
Try
Dim scanbox As New TextBox
Dim read As String = My.Computer.FileSystem.ReadAllText(System.AppDomain.CurrentDomain.BaseDirectory & "db1.db")
scanbox.Text = read.ToString
Dim md5 As MD5CryptoServiceProvider = New MD5CryptoServiceProvider
Dim f As FileStream = New FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read, 8192)
f = New FileStream(file, FileMode.Open, FileAccess.Read, FileShare.Read, 8192)
md5.ComputeHash(f)
Dim hash As Byte() = md5.Hash
Dim buff As StringBuilder = New StringBuilder
Dim hashByte As Byte
For Each hashByte In hash
buff.Append(String.Format("{0:X2}", hashByte))
Next
If scanbox.Text.Contains(buff.ToString) Then
AddListItem(Listbox2, "" & file)
AddListItem2(Listbox2, "" & file & "")
End If
Catch ex As Exception
End Try
If (Backgroundworker1.CancellationPending = True) Then
e.Cancel = True
Exit For
End If
SetLabelText_ThreadSafe(Me.Labelscannedfiles, file & "")
i = i + 1
SetLabelText_ThreadSafe(Me.Label2, i)
Dim pct As Integer = CInt(CDbl(i) / CDbl(files.Count) * 100)
Backgroundworker1.ReportProgress(pct)
Next file
Catch ex As UnauthorizedAccessException
End Try
Private Sub BackgroundWorker1_ProgressChanged(ByVal sender As Object, ByVal e As System.ComponentModel.ProgressChangedEventArgs) Handles Backgroundworker1.ProgressChanged
Label28.Text = "Computer scan is in progress... Please wait"
ProgressBar1.Value = e.ProgressPercentage / 100
Any solution or help would be highly appreciated!
In your DoWork() method...
Change:
For Each file As String In GetFilesRecursive(FolderBrowserDialog1.SelectedPath)
To:
Dim i As Integer ' <- you didn't actually declare a counter before
Dim files As List(Of String) = GetFilesRecursive(FolderBrowserDialog1.SelectedPath)
For Each file As String In files
Now you can get the count from "files" and use that in your percentage calculation:
i = i + 1 ' <-- increment our "i" counter
SetLabelText_ThreadSafe(Me.Label2, i)
Dim pct As Integer = CInt(CDbl(i) / CDbl(files.Count) * 100)
backgroundworker1.ReportProgress(pct)
I am creating a file downloader using backgroundworker & i want to save the progress of backgroundworker so that when i restart the application, on resume, it start the download where i closed last time.
Any help ?
Here is My code for Downloader
Public Class cls_FileDownloader
Dim FileSave As String
Delegate Sub DownloadFIlesafe(ByVal Cancelled As Boolean)
Delegate Sub ChangeTextsSafe(ByVal length As Long, ByVal position As Double, ByVal percent As Long, ByVal speed As Long)
Private Sub cls_FileDownloader_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Label1.Text = ""
btn_Cancel.Enabled = False
End Sub
Private Sub btn_Download_Click(sender As Object, e As EventArgs) Handles btn_Download.Click
If Me.txt_Url.Text <> "" Then
Me.SaveFile.FileName = txt_Url.Text.Split("\"c)(txt_Url.Text.Split("\"c).Length - 1)
If SaveFile.ShowDialog = System.Windows.Forms.DialogResult.OK Then
FileSave = SaveFile.FileName
SaveFile.FileName = ""
lbl_SaveTO.Text = "Save To:" & FileSave
txt_Url.Enabled = False
btn_Download.Enabled = False
btn_Cancel.Enabled = True
bg_Worker.RunWorkerAsync()
End If
Else
MessageBox.Show("Please Insert valid file path", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning)
End If
End Sub
Private Sub bg_Worker_DoWork(sender As Object, e As DoWorkEventArgs) Handles bg_Worker.DoWork
Dim response As FileWebResponse
Dim request As FileWebRequest
Try
request = WebRequest.Create(txt_Url.Text)
response = request.GetResponse
Catch ex As Exception
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error)
Dim cancelDelegate As New DownloadFIlesafe(AddressOf Download_Complete)
Me.Invoke(cancelDelegate, True)
Exit Sub
End Try
Dim length As Long = response.ContentLength
Dim safedelegate As New ChangeTextsSafe(AddressOf ChangeTexts)
Me.Invoke(safedelegate, length, 0, 0, 0)
Dim writeStream As New IO.FileStream(Me.FileSave, IO.FileMode.Create)
Dim nRead As Double
'To calculate the download speed
Dim speedtimer As New Stopwatch
Dim currentspeed As Long = -1
Dim readings As Integer = 0
Do
If bg_Worker.CancellationPending Then 'If user abort download
Exit Do
End If
speedtimer.Start()
Dim readBytes(4095) As Byte
Dim bytesread As Long = response.GetResponseStream.Read(readBytes, 0, 4096)
nRead += bytesread
Dim percent As Long = (nRead * 100) / length
Me.Invoke(safedelegate, length, nRead, percent, currentspeed)
If bytesread = 0 Then Exit Do
writeStream.Write(readBytes, 0, bytesread)
speedtimer.Stop()
readings += 1
If readings >= 5 Then 'For increase precision, the speed it's calculated only every five cicles
currentspeed = 20480 / (speedtimer.ElapsedMilliseconds / 1000)
speedtimer.Reset()
readings = 0
End If
Loop
'Close the streams
response.GetResponseStream.Close()
writeStream.Close()
If Me.bg_Worker.CancellationPending Then
IO.File.Delete(Me.FileSave)
Dim cancelDelegate As New DownloadFIlesafe(AddressOf Download_Complete)
Me.Invoke(cancelDelegate, True)
Exit Sub
End If
Dim completeDelegate As New DownloadFIlesafe(AddressOf Download_Complete)
Me.Invoke(completeDelegate, False)
End Sub
I am trying to make a download client with ftp and im still trying to find out whats the best i have but im in stuck because i followed a guide but it only show how much its have downloaded in bytes, how can i change it to gb? and how can i add a download speed?
Imports System.Net
Public Class MainForm
Private Sub MainForm_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Control.CheckForIllegalCrossThreadCalls = False
End Sub
Private Sub btnBrowseSave_Click(sender As Object, e As EventArgs) Handles btnBrowseSave.Click
Dim newFolder As New FolderBrowserDialog
If newFolder.ShowDialog = Windows.Forms.DialogResult.OK Then
txtSavePath.Text = newFolder.SelectedPath
End If
End Sub
Private Sub btnDownload_Click(sender As Object, e As EventArgs) Handles btnDownload.Click
bWorker.RunWorkerAsync()
End Sub
Private Sub bWorker_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles bWorker.DoWork
Dim buffer(1023) As Byte
Dim bytesIn As Integer
Dim totalBytesIn As Integer
Dim output As IO.Stream
Dim flLength As Integer
Try
Dim FTPRequest As FtpWebRequest = DirectCast(WebRequest.Create(txtFilePath.Text), FtpWebRequest)
FTPRequest.Credentials = New NetworkCredential(txtFTPUsername.Text, txtFTPPassword.Text)
FTPRequest.Method = Net.WebRequestMethods.Ftp.GetFileSize
flLength = CInt(FTPRequest.GetResponse.ContentLength)
lblFileSize.Text = flLength & " bytes"
Catch ex As Exception
End Try
Try
Dim FTPRequest As FtpWebRequest = DirectCast(WebRequest.Create(txtFilePath.Text), FtpWebRequest)
FTPRequest.Credentials = New NetworkCredential(txtFTPUsername.Text, txtFTPPassword.Text)
FTPRequest.Method = WebRequestMethods.Ftp.DownloadFile
Dim stream As System.IO.Stream = FTPRequest.GetResponse.GetResponseStream
Dim OutputFilePath As String = txtSavePath.Text & "\" & IO.Path.GetFileName(txtFilePath.Text)
output = System.IO.File.Create(OutputFilePath)
bytesIn = 1
Do Until bytesIn < 1
bytesIn = stream.Read(buffer, 0, 1024)
If bytesIn > 0 Then
output.Write(buffer, 0, bytesIn)
totalBytesIn += bytesIn
lblDownloadedBytes.Text = totalBytesIn.ToString & " bytes"
If flLength > 0 Then
Dim perc As Integer = (totalBytesIn / flLength) * 100
bWorker.ReportProgress(perc)
End If
End If
Loop
output.Close()
stream.Close()
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
Private Sub bWorker_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles bWorker.ProgressChanged
pBar.Value = e.ProgressPercentage
lblPercent.Text = e.ProgressPercentage.ToString & "%"
End Sub
Private Sub bWorker_RunWorkerCompleted(sender As Object, e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bWorker.RunWorkerCompleted
MsgBox("Download Complete!")
End Sub
End Class
1 KB = 210 bytes = 1,024 bytes
1 MB = 210 KB = 220 bytes = 1,048,576 bytes
1 GB = 210 MB = 230 bytes = 1,073,741,824 bytes
1 TB = 210 GB = 240 bytes = 1,099,511,627,776 bytes
The next units are Petabytes PB and Exabytes EB (maybe in future computer generations?).
Here is a function that formats the byte numbers. It determines the appropriate unit automatically:
Public Function FormatBytes(ByVal bytes As Long) As String
Dim sizes As String() = {"B", "KB", "MB", "GB", "TB", "PB", "EB"}
Dim order As Integer = 0
Dim len As Double = bytes
While len >= 1024 AndAlso order + 1 < sizes.Length
order += 1
len = len / 1024
End While
Return String.Format("{0:0.0} {1}", len, sizes(order))
End Function
Initialize the worker with:
bWorker.WorkerReportsProgress = True
You can also pass the number of bytes instead of a percentage to the ReportProgress method. The BackgroundWorker doesn't care. Because big files might exceed Integer.MaxValue declare totalBytesIn As Long instead as of Integer and pass the progress in KB to the ReportProgress method:
bWorker.ReportProgress(CInt(totalBytesIn / 1024))
In the ProgressChanged event handler:
Private Sub bWorker_ProgressChanged(ByVal sender As Object, _
ByVal e As ProgressChangedEventArgs) _
Handles bWorker.ProgressChanged
Me.txtProgress.Text = FormatBytes(1024 * e.ProgressPercentage)
' More code to come here ... (see below)
End Sub
As for the speed you could use a System.Diagnostics.Stopwatch. It shows you the elapsed time.
Dim stopwatch As New System.Diagnostics.Stopwatch()
stopwatch.Start()
And then in bWorker_ProgressChanged:
Dim elapsedSeconds As Double = stopwatch.ElapsedMilliseconds / 1000
If elapsedSeconds > 0 Then
Dim speed As Double = bytes / elapsedSeconds
lblSpeed.Text = FormatBytes(CInt(speed)) & " / s"
End If
The final version of the progress changed event handler:
Private Sub bWorker_ProgressChanged(ByVal sender As Object, _
ByVal e As System.ComponentModel.ProgressChangedEventArgs) _
Handles bWorker.ProgressChanged
Dim bytes As Long = 1024 * e.ProgressPercentage
'Can exceed 100% beacause of rounding effects
Dim percent As Integer = Math.Min(100, CInt(100 * bytes \ flLength))
pBar.Value = percent
lblPercent.Text = percent & "%"
lblDownloadedBytes.Text = FormatBytes(bytes)
Dim elapsedSeconds As Double = stopwatch.ElapsedMilliseconds / 1000
If elapsedSeconds > 0 Then
Dim speed As Double = bytes / elapsedSeconds
lblSpeed.Text = FormatBytes(CInt(speed)) & " / s"
End If
End Sub
And if you declare flLength in the Form instead as local variable, you can calculate and display the percentages in bWorker_ProgressChanged as well.
As you have troubles understanding my answer, here is the complete code. Where possible I kept your code as is and marked my changes with comments ('===). (I had only my code snippets from above until now, so I had to create a form with all the required controls. It's tested and it works):
Imports System.Net
Public Class MainForm
Dim flLength As Long '=== As Form variable instead as local variable and As Long.
Dim stopwatch As New System.Diagnostics.Stopwatch() '=== New variable.
Private Sub MainForm_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
Control.CheckForIllegalCrossThreadCalls = False
End Sub
Private Sub btnBrowseSave_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnBrowseSave.Click
Dim newFolder As New FolderBrowserDialog
If newFolder.ShowDialog = Windows.Forms.DialogResult.OK Then
txtSavePath.Text = newFolder.SelectedPath
End If
End Sub
Private Sub btnDownload_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnDownload.Click
bWorker.RunWorkerAsync()
End Sub
Private Sub bWorker_DoWork(ByVal sender As Object, ByVal e As System.ComponentModel.DoWorkEventArgs) Handles bWorker.DoWork
Dim buffer(1023) As Byte
Dim bytesIn As Integer
Dim totalBytesIn As Long '=== As Long instead of Integer
Dim output As IO.Stream
'Dim flLength As Integer '=== Now as form variable
Try
Dim FTPRequest As FtpWebRequest = DirectCast(WebRequest.Create(txtFilePath.Text), FtpWebRequest)
FTPRequest.Credentials = New NetworkCredential(txtFTPUsername.Text, txtFTPPassword.Text)
FTPRequest.Method = Net.WebRequestMethods.Ftp.GetFileSize
flLength = CInt(FTPRequest.GetResponse.ContentLength)
lblFileSize.Text = flLength & " bytes"
Catch ex As Exception
End Try
Try
stopwatch.Start() '=== Start Stopwatch
Dim FTPRequest As FtpWebRequest = DirectCast(WebRequest.Create(txtFilePath.Text), FtpWebRequest)
FTPRequest.Credentials = New NetworkCredential(txtFTPUsername.Text, txtFTPPassword.Text)
FTPRequest.Method = WebRequestMethods.Ftp.DownloadFile
Dim stream As System.IO.Stream = FTPRequest.GetResponse.GetResponseStream
Dim OutputFilePath As String = txtSavePath.Text & "\" & IO.Path.GetFileName(txtFilePath.Text)
output = System.IO.File.Create(OutputFilePath)
bytesIn = 1
Do Until bytesIn < 1
bytesIn = stream.Read(buffer, 0, 1024)
If bytesIn > 0 Then
output.Write(buffer, 0, bytesIn)
totalBytesIn += bytesIn
lblDownloadedBytes.Text = totalBytesIn.ToString & " bytes"
If flLength > 0 Then
'=== Moved to bWorker_ProgressChanged.
'Dim perc As Integer = (totalBytesIn / flLength) * 100
'=== Now reports downloaded bytes in KB instead of percent.
bWorker.ReportProgress(CInt(totalBytesIn / 1024))
End If
End If
Loop
output.Close()
stream.Close()
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
'=== Completely reworked
Private Sub bWorker_ProgressChanged(ByVal sender As Object, _
ByVal e As System.ComponentModel.ProgressChangedEventArgs) _
Handles bWorker.ProgressChanged
Dim bytes As Long = 1024 * e.ProgressPercentage
'Can exceed 100% beacause of rounding effects
Dim percent As Integer = Math.Min(100, CInt(100 * bytes \ flLength))
pBar.Value = percent
lblPercent.Text = percent & "%"
lblDownloadedBytes.Text = FormatBytes(bytes)
Dim elapsedSeconds As Double = stopwatch.ElapsedMilliseconds / 1000
If elapsedSeconds > 0 Then
Dim speed As Double = bytes / elapsedSeconds
lblSpeed.Text = FormatBytes(CInt(speed)) & " / s"
End If
End Sub
Private Sub bWorker_RunWorkerCompleted(ByVal sender As Object, ByVal e As System.ComponentModel.RunWorkerCompletedEventArgs) Handles bWorker.RunWorkerCompleted
MsgBox("Download Complete!")
End Sub
'=== New Function
Private Function FormatBytes(ByVal bytes As Long) As String
Dim sizes As String() = {"B", "KB", "MB", "GB", "TB", "PB", "EB"}
Dim order As Integer = 0
Dim len As Double = bytes
While len >= 1024 AndAlso order + 1 < sizes.Length
order += 1
len = len / 1024
End While
Return String.Format("{0:0.0} {1}", len, sizes(order))
End Function
End Class
See: How to: Use a Background Worker
I am making an application that involves appending a large number of files to the end of one single file... i am using the filestream class with a buffer to avoid loading the entire files into memory, however, i want to show the progress of each individual file as they are copied, and the name of the current file... this is easy enough, however, doing so inside the foreach loop seams to decrease the preformance dramatically if each file is very small.
here is the code:
Public Function StreamAppendFileToFile(ByVal f1 As String, ByVal f2 As String)
Dim bytesRead As Integer
Dim nn As New FileInfo(f1)
CurrentFsize = nn.Length
Dim buffer(40096) As Byte
Using inFile As New System.IO.FileStream(f1, IO.FileMode.Open, IO.FileAccess.Read)
Using outFile As New System.IO.FileStream(f2, IO.FileMode.Append, IO.FileAccess.Write)
Do
bytesRead = inFile.Read(buffer, 0, 40096)
outFile.Write(buffer, 0, bytesRead)
Application.DoEvents()
Loop While bytesRead > 0
End Using
End Using
End Function
if i put something like this the execution time doubles:
Public Function StreamAppendFileToFile(ByVal f1 As String, ByVal f2 As String)
Dim bytesRead As Integer
Dim nn As New FileInfo(f1)
CurrentFsize = nn.Length
Dim buffer(40096) As Byte
Using inFile As New System.IO.FileStream(f1, IO.FileMode.Open, IO.FileAccess.Read)
Using outFile As New System.IO.FileStream(f2, IO.FileMode.Append, IO.FileAccess.Write)
Do
bytesRead = inFile.Read(buffer, 0, 40096)
**Progressbar1.value = Math.Round((bytesRead / CurrentFsize) * 100)**
**Application.Doevents()**
outFile.Write(buffer, 0, bytesRead)
Application.DoEvents()
Loop While bytesRead > 0
End Using
End Using
End Function
is there a better/faster/more efficiant way of doing this both in terms of stream-appending one file to the other and showing the progress? thanks..
If the operation takes a lot of time, I suggest to move the operation to another thread. You can use for example the BackgroundWorker to do this (and use the events DoWork to perform the action and ProgressChanged event to report the progress to the UI).
Code example:
First make sure you have a BackgroundWorker and it is set to report progress (by setting the property WorkerReportsProgress to True)
' This class is used to pass the information to the BackgroundWorker DoWork event
Private Class IOFilesInfo
Public Property InFile As String
Public Property OutFile As String
End Class
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
' Start the BackgroundWorker if it isn't started yet
If Not BackgroundWorker1.IsBusy Then
Dim filesInfo As New IOFilesInfo() With {.InFile = "in.txt", .OutFile = "out.txt"}
BackgroundWorker1.RunWorkerAsync(filesInfo)
End If
End Sub
Private Sub BackgroundWorker1_DoWork(sender As System.Object, e As System.ComponentModel.DoWorkEventArgs) Handles BackgroundWorker1.DoWork
Dim worker As BackgroundWorker = DirectCast(sender, BackgroundWorker)
Dim filesInfo As IOFilesInfo = DirectCast(e.Argument, IOFilesInfo)
' Get the file info for the input file (the filesize)
Dim inFileFileInfo As New FileInfo(filesInfo.InFile)
Dim inFileFileSize As Long = inFileFileInfo.Length
' Keep the progress, total amount of bytes read => you could also keep the progress percentage
Dim totalBytesRead As Integer = 0
Dim bytesRead As Integer
Dim buffer(10) As Byte
Using inFile As New System.IO.FileStream(filesInfo.InFile, IO.FileMode.Open, IO.FileAccess.Read)
Using outFile As New System.IO.FileStream(filesInfo.OutFile, IO.FileMode.Append, IO.FileAccess.Write)
Do
bytesRead = inFile.Read(buffer, 0, 10)
outFile.Write(buffer, 0, bytesRead)
' Add the bytesRead to the total and report the progress as a percentage
totalBytesRead += bytesRead
worker.ReportProgress(Convert.ToInt32(totalBytesRead \ inFileFileSize) * 100)
Loop While bytesRead > 0
End Using
End Using
End Sub
Private Sub BackgroundWorker1_ProgressChanged(sender As System.Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BackgroundWorker1.ProgressChanged
ProgressBar1.Value = e.ProgressPercentage
End Sub