VB.NET AddHandler Download Progress for multiple files - vb.net

I trying to write an app to download 2 files, I'm able to download the 2 files with the code below:
Dim client As WebClient = New WebClient
AddHandler client.DownloadProgressChanged, AddressOf client_ProgressChanged
AddHandler client.DownloadFileCompleted, AddressOf client_DownloadCompleted
client.DownloadFileAsync(New Uri("http://URL.com/Myfile.exe"), "..\MyFile.exe")
Button1.Text = "Download in Progress"
Button1.Enabled = False
Dim client2 As WebClient = New WebClient
AddHandler client2.DownloadProgressChanged, AddressOf client_ProgressChanged
AddHandler client2.DownloadFileCompleted, AddressOf client_DownloadCompleted
client2.DownloadFileAsync(New Uri("http://URL.com/Myfile2.exe"), "..\MyFile2.exe")
Problem I'm having is that the progress bar is not showing total download progress for both files. It shows one, then a few seconds later shows the other one and keeps switching between both.
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
ProgressBar.Value = Int32.Parse(Math.Truncate(percentage).ToString())
Label1.Text = "Downloaded: " & bytesIn & " of " & totalBytes
Label2.Text = String.Format("{0:00}", percentage) & "%"
End Sub
Anyone know how I can make the progressbar have the values of both downloads combined? Or maybe how I can tell it to wait for the first download then start the second?

You simply need to keep track of progress for each WebClient. Total progress is going to be 200%. Solving it for the general case:
Dim progress As Dictionary(Of WebClient, Integer)
Sub StartDownloads()
progress = new Dictionary(Of WebClient, Integer)
Dim client As WebClient = New WebClient()
progress.Add(client, 0)
'' etc..
End Sub
Private Sub client_ProgressChanged(ByVal sender As Object, ByVal e As DownloadProgressChangedEventArgs)
progress(DirectCast(sender, WebClient)) = e.ProgressProcent
Dim total As Integer = 0
For Each client In progress.Keys
total += progress(client)
Next
ProgressBar.Value = total \ progress.Count
End Sub
Untested, ought to be close.

To make it load one file at a time, use a List(Of String) for all the files names. Start the process using the first element(filename) use the completed event and remove that file name and the addhandler for the completed event. Then check if there are more files in the List(Of String) then charge up a new WebClient for that one - rinse repeat.
Private files As New List(Of String)
'fill this with the file names(URIs) then start the process
Private Sub DownloadFile()
Dim client As New WebClient
AddHandler client.DownloadProgressChanged, AddressOf client_ProgressChanged
AddHandler client.DownloadFileCompleted, AddressOf client_DownloadCompleted
client.DownloadFileAsync(New Uri(files(0), "some destination path")
End Sub
Private Sub client_DownloadCompleted(...)
RemoveHandler client.DownloadProgressChanged, AddressOf client_ProgressChanged
RemoveHandler client.DownloadFileCompleted, AddressOf client_DownloadCompleted
files.RemoveAt(0)
If files.Count > 0 Then DownloadFile()
End Sub

Related

BackgroundWorker UI and Progress issues

So I've been fiddling with this for a while and I don't know if I'm not understanding how the BackgroundWorker works and/or I'm using it wrong or if I'm missing something.
Basically what I'm trying to do is call a BackgroundWorker from a DragDrop function where the user can drop a set of images into the form. The BackgroundWorder then copies the images to a temp location thumbnails are pulled and turned into PictureBoxes and the PictureBoxes are added to a collection. Once the BackgroundWorker is completed the function runs to add all the picture boxes to the form.
All of this is working properly except the progress. The progress function doesn't like to fire until near the end (after almost all the pictures have been copied) during this time the UI is locked (which I'm sure is why the progress function isn't firing) I just can't figure out why the UI is locking.
I've stepped through the code and the ReportProgress method is being called ever loop but the ProgressReported function isn't called until near the end.
HELP! LOL
this is the ControlClass for my copying and creating thumbnails
Imports System.ComponentModel
Imports System.IO
Namespace ThumbnailViewer
Public Class ThumbnailControl
Inherits FlowLayoutPanel
Private ImageExtensions As List(Of String) = New List(Of String) From {".JPG", ".JPE", ".BMP", ".GIF", ".PNG"}
Private tempStoragePath As String = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) & "\tempPhotos"
Private WithEvents bkWPhotos As New BackgroundWorker
Public Property iThumbList As List(Of PictureBox)
Public Property sImageList As List(Of String(,))
Private PopupPrg As PopUpProgress.PopUpProgressControl
Public Sub New()
Me.AutoScroll = True
Me.AllowDrop = True
Me.DoubleBuffered = True
iThumbList = New List(Of PictureBox)()
sImageList = New List(Of String(,))()
AddHandler Me.DragDrop, AddressOf ThumbnailViewerControl_DragDrop
AddHandler Me.DragEnter, AddressOf ThumbnailViewerControl_DragEnter
If Not Directory.Exists(tempStoragePath) Then Directory.CreateDirectory(tempStoragePath)
bkWPhotos.WorkerReportsProgress = True
bkWPhotos.WorkerSupportsCancellation = True
End Sub
Public Sub BackGroundWorker_DoWork(ByVal sender As Object, ByVal e As DoWorkEventArgs) Handles bkWPhotos.DoWork
AddImage(e.Argument)
End Sub
Public Sub BackGroundWorkder_Progress(ByVal sender As Object, ByVal e As ProgressChangedEventArgs) Handles bkWPhotos.ProgressChanged
PopupPrg.SetProgress(e.ProgressPercentage)
End Sub
Public Sub BackGroundWorker_Complete(ByVal sender As Object, ByVal e As RunWorkerCompletedEventArgs) Handles bkWPhotos.RunWorkerCompleted
For Each i As PictureBox In iThumbList
Me.Controls.Add(i)
Next
PopupPrg.Destory()
Me.Cursor = Cursors.Default
End Sub
Public Sub AddImage(ByVal files As String())
Dim fImage As Image
Dim prg As Integer = 0
For Each f As String In files
If ImageExtensions.Contains(Path.GetExtension(f).ToUpperInvariant()) Then
bkWPhotos.ReportProgress(prg)
fImage = Image.FromFile(f)
File.Copy(f, tempStoragePath & "\" & Path.GetFileName(f), True)
sImageList.Add({{tempStoragePath & "\" & Path.GetFileName(f), fImage.Size.Width, fImage.Size.Height}})
Dim t As PictureBox = MakeThumbnail(fImage)
prg = prg + 1
GC.GetTotalMemory(True)
End If
Next
End Sub
Public Function MakeThumbnail(ByVal inImage As Image) As PictureBox
Dim thumb As PictureBox = New PictureBox()
thumb.Size = ScaleImage(inImage.Size, 200)
thumb.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
thumb.SizeMode = PictureBoxSizeMode.Zoom
AddHandler thumb.MouseEnter, AddressOf thumb_MouseEnter
AddHandler thumb.MouseLeave, AddressOf thumb_MouseLeave
AddHandler thumb.DoubleClick, AddressOf thumb_DoubleClick
thumb.Image = inImage.GetThumbnailImage(thumb.Width - 2, thumb.Height - 2, Nothing, New IntPtr())
iThumbList.Add(thumb)
Return thumb
End Function
Private Sub thumb_DoubleClick(ByVal sender As Object, ByVal e As EventArgs)
Dim previewForm As Form = New Form()
Dim index As Integer = Me.Controls.GetChildIndex(CType(sender, PictureBox))
Dim img As Image = Image.FromFile(sImageList(index)(0, 0))
previewForm.FormBorderStyle = FormBorderStyle.SizableToolWindow
previewForm.MinimizeBox = False
previewForm.Size = ScaleImage(img.Size, Screen.GetWorkingArea(Me).Height / 4 * 3)
previewForm.StartPosition = FormStartPosition.CenterScreen
Dim view As PictureBox = New PictureBox()
view.Dock = DockStyle.Fill
view.Image = Image.FromFile(sImageList(index)(0, 0))
view.SizeMode = PictureBoxSizeMode.Zoom
previewForm.Controls.Add(view)
previewForm.ShowDialog()
End Sub
Private Sub thumb_MouseLeave(ByVal sender As Object, ByVal e As EventArgs)
CType(sender, PictureBox).Invalidate()
End Sub
Private Sub thumb_MouseEnter(ByVal sender As Object, ByVal e As EventArgs)
Dim rc = (CType(sender, PictureBox)).ClientRectangle
rc.Inflate(-2, -2)
ControlPaint.DrawBorder((CType(sender, PictureBox)).CreateGraphics(), (CType(sender, PictureBox)).ClientRectangle, Color.Red, ButtonBorderStyle.Solid)
ControlPaint.DrawBorder3D((CType(sender, PictureBox)).CreateGraphics(), rc, Border3DStyle.Bump)
End Sub
Private Sub ThumbnailViewerControl_DragEnter(ByVal sender As Object, ByVal e As DragEventArgs)
If e.Data.GetDataPresent(DataFormats.FileDrop) Then e.Effect = DragDropEffects.Copy Else e.Effect = DragDropEffects.None
End Sub
Private Sub ThumbnailViewerControl_DragDrop(ByVal sender As Object, ByVal e As DragEventArgs)
If e.Data.GetDataPresent(DataFormats.FileDrop) Then
Dim files As String() = CType(e.Data.GetData(DataFormats.FileDrop), String())
Me.Cursor = Cursors.WaitCursor
PopupPrg = New PopUpProgress.PopUpProgressControl(Me, files.Count)
bkWPhotos.RunWorkerAsync(files)
End If
End Sub
Public Function ScaleImage(ByVal oldImage As Size, ByVal TargetHeight As Integer) As Size
Dim NewHeight As Integer = TargetHeight
Dim NewWidth As Integer = NewHeight / oldImage.Height * oldImage.Width
NewHeight = NewWidth / oldImage.Width * oldImage.Height
Return New Size(NewWidth, NewHeight)
End Function
End Class
End Namespace
.... FacePalm.. I figured it out. Apparently during my testing (before I decided to use this control and a background worker, I had added another drag drop function in another area of my code that was being called first. It was taking all the dragged images and turning them into Image data types. The rest of the function was commented out which is why I didn't notice it before because I was only stepping though the classes functions not the functions in the main UI. but it makes perfect sense now, the backgroundworker and the UI function were kicking off at the same time but while the UI thread was processing the Image data typing the report progress calls were stacking up.
After removing that secondary function it works exactly as it should, UI remains fully functional Images and PictureBoxes are processed in the background and the Progressbar updates properly and remains functional as well.

How to detect if DownloadString Failed?

I'm trying to put my External IP in a Label, so I'm downloading the result based on a site, but the moment I change my IP via VPN, the application crashes, I believe it's because it does not check if it's getting the string down, that he tried.
On form load...
If Label1.Text = LastIp Then
Try
Dim wc As New WebClient
Label1.Text = wc.DownloadString("http://icanhazip.com/")
If you can not then
Label.Text = "Failed to get IP"
End if
End If
Form...
You could set up a callback and specify the path to the file:
Dim wc As New WebClient
AddHandler wc.DownloadStringCompleted, AddressOf DownloadStringComplete
wc.DownloadStringAsync(New Uri("http://icanhazip.com/myfile.txt", UriKind.Absolute))
Private Sub DownloadStringComplete(ByVal sender As Object, ByVal e As DownloadStringCompletedEventArgs)
If Not IsNothing(e.Error) Then
Label1.Text = e.Error.Message
ElseIf e.Cancelled Then
Label1.Text = "canceled"
Else
Label1.Text = e.Result
End If
End Sub

VB.NET Progress bar on ftp upload

Hi I am trying to add a progress bar to a FTP image upload, though I looked at many examples over the internet and I've tried numerous things but just couldn't get anything to work with my following code. I want a progress bar to track the progress of the upload then show 100% once done.
My coding:
Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
Dim s As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
Static r As New Random
Dim sb As New StringBuilder
For i As Integer = 1 To 8
Dim idx As Integer = r.Next(0, 35)
sb.Append(s.Substring(idx, 1))
Next
Clipboard.SetText("http://my-site.com/osdf/" + sb.ToString() + ".png")
Using ms As New System.IO.MemoryStream
sc.CaptureDeskTopRectangle(Me.boundsRect).Save(ms, System.Drawing.Imaging.ImageFormat.Png)
''Close()
Using wc As New System.Net.WebClient
AddHandler wc.UploadDataCompleted, AddressOf UploadCompleted
wc.UploadData("ftp://USERNAME:PASSWORD#My-site.com/pic/uploads/" + sb.ToString() + ".png", ms.ToArray())
End Using
MsgBox("File Uploaded", MsgBoxStyle.Information)
End Using
End Sub
How would I be able to do this using my code?
Thanks in advance, I will appreciate your support.
Change your code and use the WebClient event like:
Using wc As New System.Net.WebClient
AddHandler wc.UploadDataCompleted, AddressOf wc_UploadDataCompleted
AddHandler wc.UploadProgressChanged, AddressOf wc_UploadProgressChanged
wc.UploadData("ftp://USERNAME:PASSWORD#My-site.com/pic/uploads/" & sb.ToString() & ".png", ms.ToArray())
End Using
Also you need:
Private Sub wc_UploadDataCompleted(sender As Object, e As System.Net.UploadDataCompletedEventArgs)
MsgBox("File Uploaded", MsgBoxStyle.Information)
End Sub
Private Sub wc_UploadProgressChanged(sender As Object, e As System.Net.UploadProgressChangedEventArgs)
ProgressBar1.Value = e.ProgressPercentage
End Sub
you will need to use "UploadDataAsync" instead of "UploadData" to be able to see the increament in the progressbar or use a BackgroundWorker with UploadData method.

How to download .exe file with progress bar - VB 2012

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

Measuring download speed

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.