Получите размер файла на FTP-сервере и поместите его на ярлык - PullRequest
0 голосов
/ 24 августа 2018

Я пытаюсь получить размер файла, размещенного на сервере FTP, и поместить его в Label, пока `BackgroundWorker работает в фоновом режиме.

IЯ использую " Попробуйте ", чтобы получить значение, однако значение ловится с первой попытки.После загрузки, если я нажимаю, чтобы попытаться получить его снова, он работает.

Примечание : индикатор выполнения также не работает с первой попытки.

Изображение

enter image description here

Что я пробовал:

Private Sub BWorkerD_DoWork(sender As Object, e As System.ComponentModel.DoWorkEventArgs) Handles BWorkerD.DoWork

    Dim buffer(1023) As Byte
    Dim bytesIn As Integer
    Dim totalBytesIn As Integer
    Dim output As IO.Stream
    Dim flLength As Integer

    ''TRY TO GET FILE SIZE''

    Try
        Dim FTPRequest As FtpWebRequest = DirectCast(WebRequest.Create(txtFilePathD.Text), FtpWebRequest)
        FTPRequest.Credentials = New NetworkCredential(txtFTPUsernameD.Text, txtFTPPasswordD.Text)
        FTPRequest.Method = Net.WebRequestMethods.Ftp.GetFileSize

        flLength = CInt(FTPRequest.GetResponse.ContentLength)
        lblFileSizeD.Text = flLength & " bytes"

    Catch ex As Exception

    End Try

    Try
        Dim FTPRequest As FtpWebRequest = DirectCast(WebRequest.Create(txtFilePathD.Text), FtpWebRequest)
        FTPRequest.Credentials = New NetworkCredential(txtFTPUsernameD.Text, txtFTPPasswordD.Text)
        FTPRequest.Method = WebRequestMethods.Ftp.DownloadFile
        Dim stream As IO.Stream = FTPRequest.GetResponse.GetResponseStream
        Dim OutputFilePath As String = txtSavePathD.Text & "\" & IO.Path.GetFileName(txtFilePathD.Text)
        output = 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
                lblDownloadedBytesD.Text = totalBytesIn.ToString & " bytes"
                If flLength > 0 Then
                    Dim perc As Integer = (totalBytesIn / flLength) * 100
                    BWorkerD.ReportProgress(perc)
                End If
            End If
        Loop

        output.Close()
        stream.Close()

    Catch ex As Exception
        MessageBox.Show(ex.Message)
    End Try
End Sub

''UPDATE EVERY PROGRESS - DONT WORK ON FIRST TRY''

Private Sub BWorkerD_ProgressChanged(sender As Object, e As System.ComponentModel.ProgressChangedEventArgs) Handles BWorkerD.ProgressChanged

    pBarD.Value = e.ProgressPercentage
    lblPercentD.Text = e.ProgressPercentage & " %"
End Sub

1 Ответ

0 голосов
/ 25 августа 2018

Основные проблемы (установите Option Strict On, чтобы найти больше):
Вы не можете получить доступ к объектам пользовательского интерфейса из потока, отличного от потока пользовательского интерфейса.
Полученная ошибка:

Недопустимая операция между потоками:
Управление lblFileSizeD доступ из
потока, отличного от потока, в котором он был создан

Затем та же ошибка для lblDownloadedBytesD.

Кроме того, вы поглощаете свои сообщения об ошибках, используя пустой обработчик с

Catch ex As Exception

End Try

Thisаннулирует любые обработки , потому что их нет.Вы просто позволяете коду пройти мимо него без каких-либо действий.Обработчики предназначены для того, чтобы обрабатывать ошибки, а не оставлять их без контроля.

Когда вам нужно получить доступ и обновить какое-либо свойство компонента пользовательского интерфейса, используйте BackGroundWorker ReportProgress () метод.Этот метод имеет перегрузку, которая принимает параметр типа Object.Это значит, что вы можете кормить его чем угодно.Этот объект будет e.UserState в классе ReportProgress ProgressChangedEventArgs .

Метод .RunWorkerAsync () также принимает параметр объекта.Этот объект станет свойством e.Argument события BackgroundWorker.DoWork .Это дает некоторую гибкость в отношении параметров, которые вы можете фактически передать в BackGroundWorker.

Еще одна проблема: процедура загрузки Ftp не поддерживает отмену.При запуске пользователь не может остановить его.

Последняя проблема: как указано в документации, вы никогда не должны ссылаться на объект BackGroundWorker, который вы создали в своем потоке пользовательского интерфейса (форме) в егоDoWork событие.Используйте объект sender и приведите его к классу BackGroundWorker.

В этом примере все ссылки пользовательского интерфейса делегируются объекту класса, который передается в событие DoWork черезRunWorkerAsync(Object) метод (с использованием свойства e.Argument).
Объект Class обновляется с информацией о ходе выполнения и затем передается методу ReportProgress(Int32, Object), который выполняется в исходном контексте синхронизации (потоке пользовательского интерфейса, где RunWorkerAsync метод вызывается).
Пользовательский интерфейс может быть безопасно обновлен.Операции между потоками выполняться не могут.

Также реализован метод отмены.Это позволяет прервать процедуру загрузки и удалить частично загруженный файл, если он был создан.

Обработка ошибок минимальна, но вам необходимо интегрировать ее с собственными инструментами.

(я использовал те же имена для элементов управления пользовательского интерфейса, тестирование должно быть проще.)

Imports System.ComponentModel
Imports System.Globalization
Imports System.IO
Imports System.Net
Imports System.Net.Security
Imports System.Security.Cryptography.X509Certificates

Public Class frmBGWorkerDownload

    Friend WithEvents BWorkerD As BackgroundWorker
    Public Sub New()
        InitializeComponent()
        BWorkerD = New BackgroundWorker()
        BWorkerD.WorkerReportsProgress = True
        BWorkerD.WorkerSupportsCancellation = True
    End Sub

    Private Class BGWorkerObject
        Public Property UserName As String
        Public Property Password As String
        Public Property ResourceURI As String
        Public Property FilePath As String
        Public Property FileLength As Long
        Public Property DownloadedBytes As Long
        Public Property BytesToDownload As Long
    End Class

    Private Sub btnDownload_Click(sender As Object, e As EventArgs) Handles btnDownload.Click
        pBarD.Value = 0
        Dim BGWorkerObj As BGWorkerObject = New BGWorkerObject With {
            .ResourceURI = txtFilePathD.Text,
            .FilePath = Path.Combine(txtSavePathD.Text, Path.GetFileName(txtFilePathD.Text)),
            .UserName = txtFTPUsernameD.Text,
            .Password = txtFTPPasswordD.Text
        }
        AddHandler BWorkerD.DoWork, AddressOf BWorkerD_DoWork
        AddHandler BWorkerD.ProgressChanged, AddressOf BWorkerD_ProgressChanged
        AddHandler BWorkerD.RunWorkerCompleted, AddressOf BWorkerD_RunWorkerCompleted
        BWorkerD.RunWorkerAsync(BGWorkerObj)

    End Sub

    Private Sub BWorkerD_DoWork(sender As Object, e As DoWorkEventArgs)
        Dim BGW As BackgroundWorker = TryCast(sender, BackgroundWorker)
        Dim BGWorkerObj As BGWorkerObject = TryCast(e.Argument, BGWorkerObject)
        Dim FTPRequest As FtpWebRequest
        Dim BufferSize As Integer = 131072

        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls11 Or SecurityProtocolType.Tls12
        ServicePointManager.ServerCertificateValidationCallback =
            Function(snd As Object, Cert As X509Certificate, Chain As X509Chain, Err As SslPolicyErrors)
                Return True
            End Function

        FTPRequest = DirectCast(WebRequest.Create(BGWorkerObj.ResourceURI), FtpWebRequest)
        FTPRequest.Credentials = New NetworkCredential(BGWorkerObj.UserName, BGWorkerObj.Password)
        'FTPRequest.Method = WebRequestMethods.Ftp.GetFileSize
        '----------------------- UPDATE  ------------------------
        FTPRequest.Method = WebRequestMethods.Ftp.ListDirectoryDetails
        '--------------------- END UPDATE  ------------------------
        FTPRequest.EnableSsl = True

        '----------------------- UPDATE  ------------------------
        Using FtpResponse As WebResponse = FTPRequest.GetResponse
            Using DirListStream As Stream = FtpResponse.GetResponseStream()
                Using listReader As StreamReader = New StreamReader(DirListStream)
                    While Not listReader.EndOfStream
                        Dim DirContent As String = listReader.ReadLine()
                        If DirContent.Contains(Path.GetFileNameWithoutExtension(BGWorkerObj.ResourceURI)) Then
                            BGWorkerObj.FileLength = Convert.ToInt64(DirContent.Split(New String() {" "}, StringSplitOptions.RemoveEmptyEntries)(4))
                            BGW.ReportProgress(0, BGWorkerObj)
                            Exit While
                        End If
                    End While
                End Using
            End Using
        End Using
        '----------------------- END UPDATE  ------------------------

        'Using FtpResponse As WebResponse = FTPRequest.GetResponse
        '    BGWorkerObj.FileLength = Convert.ToInt64(FtpResponse.ContentLength)
        '    BGW.ReportProgress(0, BGWorkerObj)
        'End Using

        If BGW.CancellationPending Then e.Cancel = True

        Try
            FTPRequest = CType(WebRequest.Create(BGWorkerObj.ResourceURI), FtpWebRequest)
            FTPRequest.EnableSsl = True
            FTPRequest.Credentials = New NetworkCredential(BGWorkerObj.UserName, BGWorkerObj.Password)
            FTPRequest.Method = WebRequestMethods.Ftp.DownloadFile

            Using Response As FtpWebResponse = DirectCast(FTPRequest.GetResponse, FtpWebResponse)
                If Response.StatusCode > 299 Then
                    e.Result = 0
                    Throw New Exception("The Ftp Server rejected the request. StatusCode: " &
                                        Response.StatusCode.ToString(),
                                        New InvalidOperationException(Response.StatusCode.ToString()))
                    Exit Sub
                End If
                Using stream = Response.GetResponseStream
                    Using fileStream As FileStream = File.Create(BGWorkerObj.FilePath)
                        Dim read As Integer
                        Dim buffer As Byte() = New Byte(BufferSize - 1) {}
                        Do
                            read = stream.Read(buffer, 0, buffer.Length)
                            fileStream.Write(buffer, 0, read)
                            BGWorkerObj.DownloadedBytes += read
                            BGWorkerObj.BytesToDownload = BGWorkerObj.FileLength - BGWorkerObj.DownloadedBytes

                            If BGW.CancellationPending Then
                                e.Cancel = True
                                Exit Do
                            Else
                                BGW.ReportProgress(CInt((CSng(BGWorkerObj.DownloadedBytes) / BGWorkerObj.FileLength) * 100), BGWorkerObj)
                            End If
                        Loop While read > 0
                    End Using
                End Using
            End Using

        Catch ex As Exception
            If e.Cancel = False Then Throw
        Finally
            If e.Cancel = True Then
                If File.Exists(BGWorkerObj.FilePath) Then
                    File.Delete(BGWorkerObj.FilePath)
                End If
            End If
        End Try

    End Sub

    Private Sub BWorkerD_ProgressChanged(sender As Object, e As ProgressChangedEventArgs)
         pBarD.Value = e.ProgressPercentage
        lblPercentD.Text = e.ProgressPercentage.ToString() & " %"

        If lblFileSizeD.Text.Length = 0 Then
            lblFileSizeD.Text = CType(e.UserState, BGWorkerObject).FileLength.ToString("N0", CultureInfo.CurrentUICulture.NumberFormat)
        End If
        lblDownloadedBytesD.Text = CType(e.UserState, BGWorkerObject).DownloadedBytes.ToString("N0", CultureInfo.CurrentUICulture.NumberFormat)
        If e.ProgressPercentage <= 15 Then
            lblDownloadedBytesD.ForeColor = Color.Red
        ElseIf e.ProgressPercentage <= 66 Then
            lblDownloadedBytesD.ForeColor = Color.Orange
        Else
            lblDownloadedBytesD.ForeColor = Color.LightGreen
        End If
    End Sub

    Private Sub BWorkerD_RunWorkerCompleted(sender As Object, e As RunWorkerCompletedEventArgs)
        Dim DownloadAborted As Boolean = False
        If e.Error IsNot Nothing Then
            DownloadAborted = True
            lblDownloadedBytesD.ForeColor = Color.Red
            lblDownloadedBytesD.Text = "Error!"
        ElseIf e.Cancelled Then
            DownloadAborted = True
            lblDownloadedBytesD.ForeColor = Color.Yellow
            lblDownloadedBytesD.Text = "Cancelled!"
            pBarD.Value = 0
            lblPercentD.Text = "0%"
        Else
            lblDownloadedBytesD.ForeColor = Color.LightGreen
            lblDownloadedBytesD.Text = "Download completed"
        End If

        RemoveHandler BWorkerD.DoWork, AddressOf BWorkerD_DoWork
        RemoveHandler BWorkerD.ProgressChanged, AddressOf BWorkerD_ProgressChanged
        RemoveHandler BWorkerD.RunWorkerCompleted, AddressOf BWorkerD_RunWorkerCompleted
    End Sub

    Private Sub btnAbortDownload_Click(sender As Object, e As EventArgs) Handles btnAbortDownload.Click
        BWorkerD.CancelAsync()
    End Sub
End Class

Визуальный результат описанной операции:

frmBGWorkerDownload Image

PasteBin конструктора формы + код .

...