| File: Microsoft\VisualBasic\MyServices\Internal\WebClientCopy.vb | Web Access |
| Project: src\winforms\src\Microsoft.VisualBasic.Forms\src\Microsoft.VisualBasic.Forms.vbproj (Microsoft.VisualBasic.Forms) |
' Licensed to the .NET Foundation under one or more agreements. ' The .NET Foundation licenses this file to you under the MIT license. Imports System.Net Imports Microsoft.VisualBasic.Devices Namespace Microsoft.VisualBasic.MyServices.Internal ''' <summary> ''' Class that controls the thread that does the actual work of downloading or uploading. ''' </summary> Friend NotInheritable Class WebClientCopy ' Dialog shown if user wants to see progress UI. Allows the user to cancel the file transfer. Private WithEvents _progressDialog As ProgressDialog ' The WebClient performs the downloading or uploading operations for us Private WithEvents _webClient As WebClient ' Keeps track of the error that happened during upload/download so we can throw it ' once we can guarantee we are back on the main thread Private _exceptionEncounteredDuringFileTransfer As Exception ' The percentage of the operation completed Private _percentage As Integer ' Used for invoking ProgressDialog.Increment Private Delegate Sub DoIncrement(Increment As Integer) ''' <summary> ''' Creates an instance of a WebClientCopy, used to download or upload a file. ''' </summary> ''' <param name="client">The <see cref="WebClient"/> used to do the downloading or uploading.</param> ''' <param name="dialog">UI for indicating progress.</param> Public Sub New(client As WebClient, dialog As ProgressDialog) Debug.Assert(client IsNot Nothing, $"No {client}") _webClient = client _progressDialog = dialog End Sub ''' <summary> ''' If the user clicks cancel on the Progress dialog, we need to cancel ''' the current async file transfer operation. ''' </summary> ''' <remarks> ''' Note: that we don't want to close the progress dialog here. Wait until ''' the actual file transfer cancel event comes through and do it there. ''' </remarks> Private Sub _progressDialog_UserHitCancel() Handles _progressDialog.UserHitCancel ' Cancel the upload/download transfer. We'll close the ProgressDialog ' as soon as the WebClient cancels the xfer. _webClient.CancelAsync() End Sub ''' <summary> ''' Handles the WebClient's DownloadFileCompleted event. ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> Private Sub _webClient_DownloadFileCompleted(sender As Object, e As ComponentModel.AsyncCompletedEventArgs) _ Handles _webClient.DownloadFileCompleted Try ' If the download was interrupted by an exception, keep track of the exception, ' which we'll throw from the main thread If e.Error IsNot Nothing Then _exceptionEncounteredDuringFileTransfer = e.Error End If If Not e.Cancelled AndAlso e.Error Is Nothing Then InvokeIncrement(100) End If Finally 'We don't close the dialog until we receive the WebClient.DownloadFileCompleted event CloseProgressDialog(_progressDialog) End Try End Sub ''' <summary> ''' Handles event WebClient fires whenever progress of download changes. ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> Private Sub _webClient_DownloadProgressChanged(sender As Object, e As DownloadProgressChangedEventArgs) _ Handles _webClient.DownloadProgressChanged InvokeIncrement(e.ProgressPercentage) End Sub ''' <summary> ''' Handles the WebClient's UploadFileCompleted event. ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> Private Sub _webClient_UploadFileCompleted(sender As Object, e As UploadFileCompletedEventArgs) _ Handles _webClient.UploadFileCompleted ' If the upload was interrupted by an exception, keep track of the exception, ' which we'll throw from the main thread Try If e.Error IsNot Nothing Then _exceptionEncounteredDuringFileTransfer = e.Error End If If Not e.Cancelled AndAlso e.Error Is Nothing Then InvokeIncrement(100) End If Finally ' We don't close the dialog until we receive the ' WebClient.DownloadFileCompleted event CloseProgressDialog(_progressDialog) End Try End Sub ''' <summary> ''' Handles event WebClient fires whenever progress of upload changes. ''' </summary> ''' <param name="sender"></param> ''' <param name="e"></param> Private Sub _webClient_UploadProgressChanged(sender As Object, e As UploadProgressChangedEventArgs) _ Handles _webClient.UploadProgressChanged Dim increment As Long = (e.BytesSent * 100) \ e.TotalBytesToSend InvokeIncrement(CInt(increment)) End Sub ''' <summary> ''' Notifies the progress dialog to increment the progress bar. ''' </summary> ''' <param name="progressPercentage">The percentage of bytes read.</param> Private Sub InvokeIncrement(progressPercentage As Integer) ' Don't invoke unless dialog is up and running If _progressDialog IsNot Nothing Then If _progressDialog.IsHandleCreated Then ' For performance, don't invoke if increment is 0 Dim increment As Integer = progressPercentage - _percentage _percentage = progressPercentage If increment > 0 Then _progressDialog.BeginInvoke( New DoIncrement(AddressOf _progressDialog.Increment), increment) End If End If End If End Sub ''' <summary> ''' Downloads a file. ''' </summary> ''' <param name="address">The source for the file.</param> ''' <param name="destinationFileName">The path and name where the file is saved.</param> Public Sub DownloadFile(address As Uri, destinationFileName As String) Debug.Assert(_webClient IsNot Nothing, $"No {NameOf(_webClient)}") Debug.Assert(address IsNot Nothing, $"No {NameOf(address)}") Dim path As String = IO.Path.GetDirectoryName(IO.Path.GetFullPath(destinationFileName)) Debug.Assert((Not String.IsNullOrWhiteSpace(destinationFileName)) _ AndAlso IO.Directory.Exists(path), $"Invalid {NameOf(path)}") ' If we have a dialog we need to set up an async download If _progressDialog IsNot Nothing Then _webClient.DownloadFileAsync(address, destinationFileName) 'returns when the download sequence is over, whether due to success, error, or being canceled _progressDialog.ShowProgressDialog() Else _webClient.DownloadFile(address, destinationFileName) End If 'Now that we are back on the main thread, throw the exception we encountered if the user didn't cancel. If _exceptionEncounteredDuringFileTransfer IsNot Nothing Then If _progressDialog Is Nothing OrElse Not _progressDialog.UserCanceledTheDialog Then Throw _exceptionEncounteredDuringFileTransfer End If End If End Sub ''' <summary> ''' Uploads a file. ''' </summary> ''' <param name="sourceFileName">The name and path of the source file.</param> ''' <param name="address">The address to which the file is uploaded.</param> Public Sub UploadFile(sourceFileName As String, address As Uri) Debug.Assert(_webClient IsNot Nothing, $"No {NameOf(_webClient)}") Debug.Assert(address IsNot Nothing, $"No {NameOf(address)}") Debug.Assert((Not String.IsNullOrWhiteSpace(sourceFileName)) _ AndAlso IO.File.Exists(sourceFileName), "Invalid file") ' If we have a dialog we need to set up an async download If _progressDialog IsNot Nothing Then _webClient.UploadFileAsync(address, sourceFileName) ' Returns when the download sequence is over, ' whether due to success, error, or being canceled _progressDialog.ShowProgressDialog() Else _webClient.UploadFile(address, sourceFileName) End If ' Now that we are back on the main thread, throw the exception we ' encountered if the user didn't cancel. If _exceptionEncounteredDuringFileTransfer IsNot Nothing Then If _progressDialog Is Nothing OrElse Not _progressDialog.UserCanceledTheDialog Then Throw _exceptionEncounteredDuringFileTransfer End If End If End Sub End Class End Namespace