Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/vb.net/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Vb.net WebClient异步下载程序工作不正常_Vb.net_Multithreading_Winforms_Asynchronous_Webclient - Fatal编程技术网

Vb.net WebClient异步下载程序工作不正常

Vb.net WebClient异步下载程序工作不正常,vb.net,multithreading,winforms,asynchronous,webclient,Vb.net,Multithreading,Winforms,Asynchronous,Webclient,所以,我已经花了一段时间研究这个问题了。我是一个业余程序员,所以我并不总是知道我做错了什么 无论如何,我最近项目的前提是: 我和我的朋友们经常玩MineCraft,但他们并不特别聪明,我们也不总是能得到mods并给他们发送链接等等。所以我想我应该编写一些程序来自动拉下MOD,使它们与服务器同步,同时获取服务器数据 我使用的是免费的FTP主机,但我不认为这是问题所在,原因将变得很清楚 基本上,我想使用progressbar和理想情况下的标签来指示整个数据块的进度(所有MOD加在一起…不超过1GB-

所以,我已经花了一段时间研究这个问题了。我是一个业余程序员,所以我并不总是知道我做错了什么

无论如何,我最近项目的前提是:

我和我的朋友们经常玩MineCraft,但他们并不特别聪明,我们也不总是能得到mods并给他们发送链接等等。所以我想我应该编写一些程序来自动拉下MOD,使它们与服务器同步,同时获取服务器数据

我使用的是免费的FTP主机,但我不认为这是问题所在,原因将变得很清楚

基本上,我想使用progressbar和理想情况下的标签来指示整个数据块的进度(所有MOD加在一起…不超过1GB-相当小)。但是,关于异步选项,我似乎遇到了一些问题:

  • 它将随机选择不下载它应该下载的文件

  • 在声明文件完整之前,它可能不会下载完整的文件

  • 当msgbox触发表示已完成下载所有项目时,progressbar可能已满50%

但是,虽然progressbar无法工作,因为Webclient的同步使用中不存在进度报告事件,但当我在BGworker中运行syncro时,它每次都会正确下载。然而,我失去了进度报告,这有点重要

因此,基本上:

  • 有没有更好的方法来实现这一点
这是最后一块我需要开始工作之前,它准备好了,所以我真的很想尝试这样做。谢谢你的帮助

编辑:更新为代码:

Public Function GetDownloadSize(ByVal URL As String) As Long
    Dim request As Net.FtpWebRequest = DirectCast(Net.WebRequest.Create(URL), Net.FtpWebRequest)
    request.Method = Net.WebRequestMethods.Ftp.GetFileSize
    request.Credentials = New Net.NetworkCredential(dl_user, dl_pass)
    Dim response As Net.FtpWebResponse = DirectCast(request.GetResponse(), Net.FtpWebResponse)
    Dim fileSize As Long = response.ContentLength
    Return fileSize
End Function

Private Sub btn_sync_Click(sender As Object, e As EventArgs) Handles btn_sync.Click
    Dim cont As DialogResult = MsgBox("Continue? " + (total_dl_size / 1000).ToString("N0") + " KB remain to be downloaded.", MsgBoxStyle.YesNo, "CAUTION!")
    If cont = DialogResult.No Then
        tb_warnings.AppendText("-ERR: User declined to synchronize files. Restart the application to sync.")
        tb_warnings.AppendText(ControlChars.NewLine)
        Label3.BackColor = Color.Firebrick
        Return
    End If
    btn_sync.Enabled = False
    btn_scan.Enabled = false
    tb_warnings.AppendText("-Deleting outmoded/unused mods. Protected mods will be kept.")
    For Each i As fdata_obj In deleted_files
        My.Computer.FileSystem.DeleteFile(mc_dir + "\mods\" + i.name)
    Next
    tb_warnings.AppendText(ControlChars.NewLine)
    tb_warnings.AppendText("-Deleting mod subdirectories to ensure no conflicts.")
    tb_warnings.AppendText(ControlChars.NewLine)

    For Each d In My.Computer.FileSystem.GetDirectories(mc_dir + "\mods")
        My.Computer.FileSystem.DeleteDirectory(d, FileIO.DeleteDirectoryOption.DeleteAllContents)
    Next

    initialize_download()


End Sub

Private Sub initialize_download()

           Dim wc As New System.Net.WebClient() ' SORRY, ASSUME THIS IS A PUBLIC VAR SO IT CAN BE REFERENCED ACROSS ITS OTHER METHODS
    AddHandler wc.DownloadProgressChanged, AddressOf OnDownloadProgressChanged
    AddHandler wc, AddressOf OnFileDownloadCompleted

    Dim usr As String = "randouser"
    Dim pass As String = "randopass"
    For Each s In (From dl As fdata_obj In new_files Select dl_server + "/mods/" + mods_dir + "/" + dl.name).ToList
        downloads.Enqueue(s)
    Next
    wc.Credentials = New Net.NetworkCredential(usr, pass)

        Dim urix As String = downloads.Dequeue
        Try
            wc.DownloadFileasync(New Uri(urix), mc_dir + "\mods\" + IO.Path.GetFileName(urix))
        Catch ex As Exception
            MsgBox(ex.Message)
            If tb_warnings.InvokeRequired = True Then
                tb_warnings.Invoke(New tb_updater(AddressOf tb_update), "-ERR: Could not download file: " + urix, urix)
            Else
                tb_warnings.AppendText("-ERR: Could not download file: " + IO.Path.GetFileName(urix))
                tb_warnings.AppendText(ControlChars.NewLine)

            End If
    end try
End Sub
Private Sub OnDownloadProgressChanged(ByVal sender As Object, ByVal e As System.Net.DownloadProgressChangedEventArgs)
    MsgBox("This is happening!")
    total_dl = total_dl + e.BytesReceived
    Dim percentage As Integer = (CType((total_dl / total_dl_size), Integer) * 100)
    if percentage > 100 then
        percentage = 100
    endif
    prog_update(percentage)

End Sub

delegate sub progress_update(byval prog as integer)
' POTENTIAL ISSUES HERE???????
private sub prog_update(byval prog as integer)
    if progressbar1.invokerequired then
        progressbar1.invoke(new prog_update(addressof progress),prog)
    else
        progressbar1.value = prog


Private Sub OnFileDownloadCompleted(ByVal sender As Net.WebClient, ByVal e As System.ComponentModel.AsyncCompletedEventArgs)

    If e.Cancelled Then
        MsgBox(e.Cancelled)
    ElseIf Not e.Error Is Nothing Then
        MsgBox(e.Error.Message)
    Else
    if downloads.count > 0 then
                Dim urix As String = downloads.Dequeue
        Try
            wc.DownloadFileasync(New Uri(urix), mc_dir + "\mods\" + IO.Path.GetFileName(urix))
        Catch ex As Exception
            MsgBox(ex.Message)
            If tb_warnings.InvokeRequired = True Then
                tb_warnings.Invoke(New tb_updater(AddressOf tb_update), "-ERR: Could not download file: " + urix, urix)
            Else
                tb_warnings.AppendText("-ERR: Could not download file: " + IO.Path.GetFileName(urix))
                tb_warnings.AppendText(ControlChars.NewLine)

            End If
        End Try
    End If

End Sub

首先,进度条不工作的主要原因是:

Dim percentage As Integer = (CType((total_dl / total_dl_size), Integer) * 100)
代码将首先计算
total\u dl/total\u dl\u size
,假设结果为0.34,然后将其转换为一个整数,结果为0(0.34向下舍入为零,因为整数没有小数),最后将0乘以100(结果仍然为0)

至于线程安全(调用),我总是使用我创建的:

Imports System.Runtime.CompilerServices

Public Module Extensions
    <Extension()> _
    Public Sub InvokeIfRequired(ByVal Control As Control, ByVal Method As [Delegate], ByVal ParamArray Parameters As Object())
        If Parameters Is Nothing OrElse _
            Parameters.Length = 0 Then Parameters = Nothing 'If Parameters is null or has a length of zero then no parameters should be passed.
        If Control.InvokeRequired = True Then
            Control.Invoke(Method, Parameters)
        Else
            Method.DynamicInvoke(Parameters)
        End If
    End Sub
End Module
您只需键入:

Me.InvokeIfRequired(AddressOf SomeMethod, params)
扩展方法将为您完成其余的工作

如果使用lambda表达式,则可以动态创建方法:

Me.InvokeIfRequired(Sub()
                        Label1.Text = "Hello world!"
                        ProgressBar1.Value += 1
                    End Sub)

现在,到你的代码

我把你的代码分开了一点,这样更容易处理。对于初学者,我没有将下载代码复制粘贴到
DownloadFileCompleted
事件处理程序,而是使用了一种更通用的方法,名为
DownloadFile()


希望这有帮助

上次我检查时,
WebClient
运行良好。请向我们显示您的代码。@VisualIncent#1:用户名不错,#2:编辑后的帖子显示代码。这个委托函数调用可能有点不正确,因为我在重写这段代码时没有考虑IDE的好处,并且不太记得它的格式。请放心,呼叫模板在我的原始代码中是正确的。1:谢谢;)2:好吧,我先启动我的电脑,然后我再试试密码。你真的不应该那样分享你的密码,即使没有什么有价值的东西。请删除评论,我有自己的FTP服务器可以试用。试用并找出问题可能需要一到两个小时,因此请耐心等待。:)杰出的我会在回家后努力实现这一点。谢谢你的帮助!使下载部分正常工作是难题的最后一部分。嗯,我仍然需要编写程序来生成配置文件,但这很简单。谢谢@斯密蒂·沃伯曼森:没问题。希望它能起作用,如果不是,就告诉我@斯密蒂·沃伯曼森:请记住,我并没有包含您的所有代码。你必须自己再添加一些部件。主要是队列,它是在
表单加载
事件中填充的,以及预下载的东西,它们应该在
按钮点击
事件中完成。不确定,但似乎是这样。invokeFrequeuired不是一个组成方法,或者至少它抛出了错误。附录:否则,代码工作得非常出色。至于为什么progbar是愚蠢的:每次报告prog时,我都使用e.bytesreceived来更新数量,所以它的总和是不断增加的。我设置了一个新的var基线,它保存了以前的下载量,然后您可以在运行时对它们求和,以获得总体dl。非常感谢。它起作用了!万岁!
Me.InvokeIfRequired(Sub()
                        Label1.Text = "Hello world!"
                        ProgressBar1.Value += 1
                    End Sub)
''' <summary>
''' Downloads a file from the specified URL with the specified credentials.
''' </summary>
''' <param name="URL">The URL of the file.</param>
''' <param name="Username">The username which to login with.</param>
''' <param name="Password">The password which to login with.</param>
''' <remarks></remarks>
Private Sub DownloadFile(ByVal URL As String, ByVal Username As String, ByVal Password As String)
    If wc.IsBusy = True Then Throw New Exception("A download is already ongoing!")

    wc.Credentials = New NetworkCredential(dl_user, dl_pass)
    total_dl_size = GetDownloadSize(URL, Username, Password)

    Try
        Dim FileName As String = Path.GetFileName(URL)
        AppendWarning("Downloading " & FileName & "...")
        wc.DownloadFileAsync(New Uri(URL), Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), FileName))
    Catch ex As Exception
        AppendWarning("-ERR: Could not download file: " & Path.GetFileName(URL))
    End Try
End Sub
''' <summary>
''' (Thread-safe) Appends a warning or status message to the "tb_warnings" text box.
''' </summary>
''' <param name="Text">The text to append.</param>
''' <remarks></remarks>
Private Sub AppendWarning(ByVal Text As String)
    Me.InvokeIfRequired(Sub() tb_warnings.AppendText(Text & Environment.NewLine))
End Sub
Private dl_user As String = "someusername"
Private dl_pass As String = "somepassword"

Private dl_urls As String() = {"URL1", "URL2"} 'Temporary. Use your own code.
Private total_dl_size As Long = 0
Private total_dl As Long = 0

Dim WithEvents wc As New System.Net.WebClient()
Dim downloads As New Queue(Of String)

Private Sub Form1_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load
    'Populate the download queue.
    downloads.Enqueue(dl_urls(0)) 'Temporary. Use your own code here.
    downloads.Enqueue(dl_urls(1))
End Sub

'The download button.
Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    'Do your pre-download stuff here.

    DownloadFile(downloads.Dequeue(), dl_user, dl_pass) 'Download the first file.
End Sub

''' <summary>
''' Downloads a file from the specified URL with the specified credentials.
''' </summary>
''' <param name="URL">The URL of the file.</param>
''' <param name="Username">The username which to login with.</param>
''' <param name="Password">The password which to login with.</param>
''' <remarks></remarks>
Private Sub DownloadFile(ByVal URL As String, ByVal Username As String, ByVal Password As String)
    If wc.IsBusy = True Then Throw New Exception("A download is already ongoing!")

    wc.Credentials = New NetworkCredential(dl_user, dl_pass) 'Set the credentials.
    total_dl_size = GetDownloadSize(URL, Username, Password) 'Get the size of the current file.

    Try
        Dim FileName As String = Path.GetFileName(URL) 'Get the current file's name.
        AppendWarning("Downloading " & FileName & "...") 'Download notice.
        wc.DownloadFileAsync(New Uri(URL), Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), FileName)) 'Download the file to the desktop (use your own path here).
    Catch ex As Exception
        AppendWarning("-ERR: Could not download file: " & Path.GetFileName(URL))
    End Try
End Sub

''' <summary>
''' (Thread-safe) Appends a warning or status message to the "tb_warnings" text box.
''' </summary>
''' <param name="Text">The text to append.</param>
''' <remarks></remarks>
Private Sub AppendWarning(ByVal Text As String)
    Me.InvokeIfRequired(Sub() tb_warnings.AppendText(Text & Environment.NewLine))
End Sub

Private Sub wc_DownloadProgressChanged(sender As Object, e As System.Net.DownloadProgressChangedEventArgs) Handles wc.DownloadProgressChanged
    Me.InvokeIfRequired(Sub()
                            Dim Progress As Integer = CType(Math.Round((e.BytesReceived * 100) / total_dl_size), Integer)
                            If Progress > 100 Then Progress = 100
                            If Progress < 0 Then Progress = 0
                            ProgressBar1.Value = Progress
                        End Sub)
End Sub

Private Sub wc_DownloadFileCompleted(sender As Object, e As System.ComponentModel.AsyncCompletedEventArgs) Handles wc.DownloadFileCompleted
    If e.Cancelled Then
        MessageBox.Show(e.Cancelled)

    ElseIf Not e.Error Is Nothing Then
        MessageBox.Show(e.Error.Message)

    Else
        If downloads.Count > 0 Then
            DownloadFile(downloads.Dequeue(), dl_user, dl_pass) 'Download the next file.
        Else
            AppendWarning("Download complete!")
        End If

    End If
End Sub
Path.Combine(Path1, Path2, Path3, ...)
Path.Combine("C:\", "Foo") 'Results in: C:\Foo
Path.Combine("C:\", "Foo", "Bar", "Hello World.txt") 'Results in: C:\Foo\Bar\Hello World.txt