【问题标题】:Get File Size on FTP Server and put it on a Label获取 FTP 服务器上的文件大小并将其放在标签上
【发布时间】:2019-01-31 02:27:48
【问题描述】:

我正在尝试获取托管在 FTP 服务器上的文件的大小,并将其放在 Label 中,而 `BackgroundWorker 在后台工作。

我正在使用“Try”来获取价值,但是价值在第一次尝试时就被捕获了。下载后,如果我再次尝试获取它,它就可以工作了。

注意:进度条在第一次尝试时也不起作用。

图片

我尝试过的:

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

【问题讨论】:

  • 首先,尝试将设置标签文本的行更改为Invoke(Sub() lblFileSizeD.Text = flLength &amp; " bytes")。您正在尝试从后台线程访问 GUI 线程拥有的标签……因此您实际上可能正在获取一个值而只是无法看到它。因此,请先尝试更改,如果您看到任何内容,请告诉我们。如果您仍然不这样做,那么至少它可以进一步缩小问题范围。
  • 感谢您的帮助,所以...再次,仅在第二次尝试时有效,当我再次单击下载按钮时。
  • 我刚刚从第一个“try”块中直接复制了您的代码,并在我自己的 ftp 凭据和路径中进行了交换,并且它运行良好……所以我不太确定还能去哪里看。你有没有逐行浏览代码?
  • 我今天尝试了很多东西,但似乎没有任何效果。下载后,如果我再次按下按钮,一切正常。我不明白。

标签: vb.net winforms ftp progress-bar backgroundworker


【解决方案1】:

主要问题(设置Option Strict On查找更多):
您无法从不同于 UI 线程的线程访问 UI 对象。
您收到的错误是:

跨线程操作无效:
控制lblFileSizeD访问自
创建线程之外的线程

然后,lblDownloadedBytesD 出现同样的错误。

此外,您正在使用一个空的处理程序来消耗您的错误消息

Catch ex As Exception

End Try

这会使任何处理无效,因为没有。您只是让代码运行通过它而不采取任何行动。处理程序是用来处理错误的,而不是让它们不受检查。

当您需要访问和更新某些 UI 组件属性时,请使用 BackGroundWorker ReportProgress() 方法。此方法有一个接受Object 类型参数的重载。意思是,你可以喂它任何东西。此对象将是 ReportProgress ProgressChangedEventArgs 类中的 e.UserState 属性。

.RunWorkerAsync() 方法也接受 Object 参数。该对象将成为BackgroundWorker.DoWork 事件的e.Argument 属性。这为您实际传递给BackGroundWorker 的参数提供了一些灵活性。

还有一个问题:Ftp 下载过程不支持取消。运行时,用户无法停止它。

最后一个问题:如文档中所述,您永远不应在其DoWork 事件中引用您在 UI 线程(表单)中实例化的 BackGroundWorker 对象。使用 sender 对象并将其转换为 BackGroundWorker 类。

在此示例中,所有 UI 引用都委托给一个 Class 对象,该对象通过 RunWorkerAsync(Object) 方法(使用 e.Argument 属性)传递给 DoWork 事件。
使用进度详细信息更新 Class 对象,然后将其馈送到 ReportProgress(Int32, Object) 方法,该方法在原始同步上下文(UI 线程,其中调用 RunWorkerAsync 方法)中运行。
UI 可以安全更新。不能发生跨线程操作。

还实现了一种取消方法。这允许中止下载过程并删除部分下载的文件(如果已创建)。

错误处理很少,但您需要与自己的工具集成。

(我为 UI 控件使用了相同的名称,应该更容易测试。)

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, 
          DirListStream As Stream = FtpResponse.GetResponseStream(), 
          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 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(), 
                  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

        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

描述的操作的视觉结果:


A PasteBin of the Form's Designer + Code.

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-30
  • 1970-01-01
  • 1970-01-01
  • 2016-03-03
  • 1970-01-01
  • 2018-05-12
  • 2019-07-08
相关资源
最近更新 更多