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
Related
I have this code to read large files and write them in bytes using buffer , I want to read a specific size. for example, I have a file of size 100 kb. I want to read the last 30kb only and save them into a new file.
I also don't fully understand how this reader work, but I want to use it so it cannot load all the file in memory.
Sub readthis(filein As String, fileout As Of String, startingposition as integer)
Dim bytesRead As Integer
Dim buffer(4096) As Byte
Using inFile As New IO.FileStream(filein, FileMode.Open)
Using outFile As New IO.FileStream(fileout, FileMode.Append, FileAccess.Write)
Do
bytesRead = inFile.Read(buffer, 0, buffer.Length)
If bytesRead > 0 Then
outFile.Write(buffer, 0, bytesRead)
End If
Loop While bytesRead > 0
End Using
End Using
End Sub
Try this:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
readthis("d:\log\tmp1.txt", "d:\log\tmp2.txt", 30)
End Sub
Sub readthis(filein As String, fileout As String, numlastbytes As Integer)
Dim fs As New IO.FileStream(filein, FileMode.Open, FileAccess.Read)
fs.Seek(-numlastbytes, SeekOrigin.End)
Dim bytearray(numlastbytes) As Byte
Dim numbytesread As Integer = fs.Read(bytearray, 0, numlastbytes)
fs.Close()
My.Computer.FileSystem.WriteAllBytes(fileout, bytearray, False)
End Sub
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 create an updater for my program which automatically download's the latest version of my program from the web. Now I want this process to be done using a progress bar (so when the download progress is at 50% the progress bar is half-way through). This is my code:
Private Sub client_ProgressChanged(ByVal sender As Object, ByVal e As DownloadProgressChangedEventArgs)
Dim bytesIn As Double = Double.Parse(e.BytesReceived.ToString())
Dim totalBytes As Double = Double.Parse(e.TotalBytesToReceive.ToString())
Dim percentage As Double = bytesIn / totalBytes * 100
client.Value = Int32.Parse(Math.Truncate(percentage).ToString())
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim url As String = "MY DOWNLOAD LINK"
'Download
Dim client As WebClient = New WebClient
AddHandler client.DownloadProgressChanged, AddressOf client_ProgressChanged
AddHandler client.DownloadFileCompleted, AddressOf client_DownloadCompleted
client.DownloadFileAsync(New Uri(url), "C:\Users\User\Desktop\BACKUP\TESTING\New folder\1.exe")
End Sub
End Class
Now I know that the place where the file is saved has been inputed manually by me , but I will change that later. My problem currently is that the file is not being downloaded. However when I change the DownloadFileAsync method to DownloadFile , my program downloads the file. However with the DownloadFile method I will not be able to use the progress bar to track the download progress. Any help is much appreciated :-)
Don't know what you mean, when you say that the file isn't downloaded? You get an Error/Exception? Nothing happens at all? Did you place breakpoints, debug.prints etc?
With VS2012 and asyn/await you can put everything into one method an keep a "linear code-flow". Import System.Threading and System.Threading.Tasks
Private Async Function DownloadWithProgress(ByVal url As String, ByVal p As ProgressBar) As Task(Of Integer)
Dim wc As Net.HttpWebRequest = DirectCast(Net.HttpWebRequest.Create(url), Net.HttpWebRequest)
Dim resp = Await (New TaskFactory(Of Net.WebResponse)).StartNew(AddressOf wc.GetResponse)
p.Value = 0
p.Maximum = CInt(resp.ContentLength)
Dim rqs = resp.GetResponseStream
Dim bufsize As Integer = 1 << 16
Dim buffer(bufsize) As Byte
Dim got As Integer = 0
Dim total As Integer = 0
Do
got = Await (New TaskFactory(Of Integer)).FromAsync(AddressOf rqs.BeginRead, AddressOf rqs.EndRead, buffer, 0, bufsize, Nothing)
total += got
Me.Label1.Text = "got: " & total.ToString
p.Increment(got)
Loop Until got = 0
Return total
End Function
In this sample, the data from the web is downloaded into an array, but you can of course also write it into a file, or do whatever you want with the data.
Sample for usage:
Private running As Boolean = False
Private Async Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
If running Then
MessageBox.Show("Cant you see, I'm working?")
Exit Sub
Else
running = True
End If
Await DownloadWithProgress("http://download.thinkbroadband.com/5MB.zip", ProgressBar1)
running = False
End Sub
I'm downloading a file using the following:
Dim client As WebClient = New WebClient()
client.DownloadFileAsync(New Uri("http://cachefly.cachefly.net/100mb.test"), "C:\Users\Dir\100mb.test")
The file downloads and saves into the C://Users/Dir/100mb.test, but while its downloading I would like to display the download speed in a label. How can I do this? I have read many tutorials, but most of them doesn't works or are outdated. I'm a newbie with vb.net, so I can't really write something on my own, could you give me any solutions?
May I suggest something different?
Imports System.Net
Public Class Form1
Private tmp = IO.Path.Combine(My.Computer.FileSystem.SpecialDirectories.Temp, "snafu.fubar")
Private Downloading As Boolean = False
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If Downloading Then Exit Sub
Downloading = True
Dim wc As New WebClient
AddHandler wc.DownloadProgressChanged, AddressOf wc_ProgressChanged
AddHandler wc.DownloadFileCompleted, AddressOf wc_DownloadDone
wc.DownloadFileAsync(New Uri("http://cachefly.cachefly.net/100mb.test"), tmp, Stopwatch.StartNew)
End Sub
Private Sub wc_DownloadDone(sender As Object, e As System.ComponentModel.AsyncCompletedEventArgs)
Downloading = False
End Sub
Private Sub wc_ProgressChanged(sender As Object, e As DownloadProgressChangedEventArgs)
Me.Label1.Text = (e.BytesReceived / (DirectCast(e.UserState, Stopwatch).ElapsedMilliseconds / 1000.0#)).ToString("#")
End Sub
End Class
Because it makes no sense to determine the speed by the last chunk of bytes received, but instead you measure the total number of bytes and divide by the TOTAL time. Passing a stopwatch instance to the eventhandler has the advantage that it doesnt 'spoil' your class code - it''s visible only where it's needed.
This will give you percentage in the mean time. You could probably calculate from the percentage given a start time using DateTime.Now the speed in which it the download is taking:
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Try
Dim client As WebClient = New WebClient()
AddHandler client.DownloadProgressChanged, AddressOf ProgChanged
client.DownloadFileAsync(New Uri("http://cachefly.cachefly.net/100mb.test"), "C:\Users\Dir\100mb.test")
Catch ex As Exception
MessageBox.Show(ex.Message)
End Try
End Sub
Private Sub ProgChanged(ByVal sender As Object, ByVal e As DownloadProgressChangedEventArgs)
Dim progressPercentage As Integer = e.ProgressPercentage
End Sub
Define globals:
Dim lastUpdate As DateTime
Dim lastBytes As Long = 0
You'll need to assign an Event for progress:
Dim client As WebClient = New WebClient()
client.DownloadFileAsync(New Uri("http://cachefly.cachefly.net/100mb.test"), "C:\Users\Dir\100mb.test")
client.DownloadProgressChanged += Function(sender, e) progressChanged(e.BytesReceived)
The Event:
Private Sub progressChanged(bytes As Long)
If lastBytes = 0 Then
lastUpdate = DateTime.Now
lastBytes = bytes
Return
End If
Dim now = DateTime.Now
Dim timeSpan = now - lastUpdate
If Not timeSpan.Seconds = 0
Dim bytesChange = bytes - lastBytes
Dim bytesPerSecond = bytesChange / timeSpan.Seconds
lastBytes = bytes
lastUpdate = now
End If
End Sub
And you have the calculations of bytes per second.
label.Text = bytesPerSecond.ToString() + "B/s";
Try this:
Try this only when you're downloading through a backgroundworker
Private Sub worker_DoWork(Byval sender As Object, Byval e As DoWorkEventArgs)
Dim req As WebRequest = WebRequest.Create(downloadUrl) 'Make a request for the url of the file to be downloaded
Dim resp As WebResponse = req.GetResponse 'Ask for the response
Dim fs As New FileStream(path to download to, FileMode.CreateNew) 'Create a new FileStream that writes to the desired download path
Dim buffer(8192) As Byte 'Make a buffer
Dim downloadedsize As Long = 0
Dim downloadedTime As Long = 0
Dim dlSpeed As Long = 0
Dim currentSize As Long = 0 'Size that has been downloaded
Dim totalSize As Long = req.ContentLength 'Total size of the file that has to be downloaded
Do While currentSize < totalSize
Dim read As Integer = resp.GetResponseStream.Read(buffer, 0, 8192) 'Read the buffer from the response the WebRequest gave you
fs.Write(buffer, 0, read) 'Write to filestream that you declared at the beginning of the DoWork sub
currentSize += read
downloadedSize += read
downloadedTime += 1 'Add 1 millisecond for every cycle the While field makes
If downloadedTime = 1000 Then 'Then, if downloadedTime reaches 1000 then it will call this part
dlSpeed = (downloadedSize / TimeSpan.FromMilliseconds(downloadedTime).TotalSeconds) 'Calculate the download speed by dividing the downloadedSize by the total formatted seconds of the downloadedTime
downloadedTime = 0 'Reset downloadedTime and downloadedSize
downloadedSize = 0
End If
End While
fs.Close() 'Close the FileStream first, or the FileStream will crash.
resp.Close() 'Close the response
End Sub
Sorry for not using proper formatting, but this is a solution in itself.
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