0

I have multiple files to upload (to FTP server) using this code:

    Private Sub UploadFile(ByVal local As String)
    If wc.IsBusy = True Then Throw New Exception("An upload is already ongoing!")

    wc.Credentials = New NetworkCredential(usr.ToString, pass.ToString) 'Set the credentials.
    'total_dl_size = GetDownloadSize(url) 'Get the size of the current file.

    Try
        Dim FileName As String = Path.GetFileName(local) 'Get the current file's name.
        AppendWarning("Uploading " & FileName & "...   ") 'Download notice.
        wc.UploadFileAsync(New Uri(info_srv & local), Path.Combine(mc_dir, local)) 'Download the file to the desktop (use your own path here).
    Catch ex As Exception
        AppendWarning("-ERR: Could not download file: " & local & ControlChars.NewLine)
    End Try
End Sub
Private Sub AppendWarning(ByVal Text As String)
    If tb_warnings.InvokeRequired Then
        tb_warnings.Invoke(Sub() tb_warnings.AppendText(Text))
    Else
        tb_warnings.AppendText(Text)
    End If

End Sub

Private Sub wc_UploadProgressChanged(sender As Object, e As System.Net.UploadProgressChangedEventArgs) Handles wc.UploadProgressChanged
    total_ul = e.BytesSent
    Dim Progress As Integer = CType(Math.Round((baseline + total_ul) * 100) / total_ul_size, Integer)
    If ProgressBar1.InvokeRequired Then
        ProgressBar1.Invoke(Sub()

                                If Progress > 100 Then Progress = 100
                                If Progress < 0 Then Progress = 0
                                ProgressBar1.Value = Progress
                            End Sub)
    Else
        If Progress > 100 Then Progress = 100
        If Progress < 0 Then Progress = 0
        ProgressBar1.Value = Progress
    End If
    If lbl_progress.InvokeRequired Then
        lbl_progress.Invoke(Sub() lbl_progress.Text = ((total_ul + baseline) / 1024).ToString("N0") & " KB / " & (total_ul_size / 1024).ToString("N0") & " KB")
    Else
        lbl_progress.Text = ((total_ul + baseline) / 1024).ToString("N0") & " KB / " & (total_ul_size / 1024).ToString("N0") & " KB | " & Progress.ToString & "%"
    End If

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

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

    Else
        If files.Count > 0 Then
            AppendWarning("Upload Complete!" & ControlChars.NewLine)
            baseline = baseline + total_ul
            Dim file As String = files.Dequeue()
            MsgBox(file)
            UploadFile(file) 'Download the next file.
        Else
            AppendWarning("All Uploads Finished!" & ControlChars.NewLine)
        End If

    End If

However, using my two test files, it always stops at what would otherwise be the end of the first file I've given it, and doesn't go onto the second one.

However, I have an FTP client connected to this same server, and when I refresh I can see (at least for the first file) the data is being properly uploaded.

Any suggestions as to what's going wrong here?

Edit, log: http://pastebin.com/kqG28NGH

Thank you for any assistance!

  • Are the `wc_UploadProgressChanged` and `wc_uploadFileCompleted` being called? Do you get the same problem with synchronous upload? What does it mean "stops" anyway? Show us a [log file](http://stackoverflow.com/q/9664650/850848). – Martin Prikryl Feb 03 '17 at 04:46
  • @MartinPrikryl progress is, completed does not seem to be. I haven't tried synchronous yet, and by stops, say I have a file of 11 KB, in my queue, as the first file, there is no apparent download progression beyond 11KB-1. It is not considering the file finished, ergo, none of the other uploads progress. I will try to upload a log when I am back on my system tomorrow. Please note: If I have said download here, it is because my brain is muddled. All such instances can be replaced with upload. – smitty_werbermanjensen Feb 03 '17 at 04:59
  • A bit off-topic from your question, but you shouldn't check `InvokeRequired` for every control. Check it once then do all the updating the same `If`-statement. You could just check `Me.InvokeRequired` since the invocation will always be done by the current form (that is, even if you call `yourControl.Invoke()`). – Visual Vincent Feb 03 '17 at 07:25
  • What I'm talking about is that you can minimize the code used to invoke by doing, for example, this: http://pastebin.com/q69FYrYj -- Apart from the reduction of code it does the exact same things as your current code _**except**_ it only invokes once. :) – Visual Vincent Feb 03 '17 at 07:37
  • @VisualVincent I have uploaded the tail end of the logs generated from a net trace. The filenames on the side are components of the .jar files the mods are stored as. The full files I was attempting to upload for test purposes are: AcademyCraft-1.0pr2_1.jar AgriCraft-1.7.10-1.4.6-hotfix – smitty_werbermanjensen Feb 04 '17 at 00:07
  • @MartinPrikryl Please see above comment. – smitty_werbermanjensen Feb 04 '17 at 00:08

2 Answers2

0

This works for me...I tried to mimic what I think is in your form. I tested with a queue of 8 files ranging from 150K to 400K each. I couldn't quite work out what you were trying to do with the progress bar. Mine fills for each file and resets for the next, finishing empty with the last call to DoUpload where there are no more files. Hopefully, this will help.

Imports System.IO
Imports System.Net

Public Class Form1
    Const info_srv As String = "ftp://example.com/SomeFolder/"
    Const usr As String = ""
    Const pass As String = ""

    Const mc_dir As String = "D:\Source\Folder"

    Private WithEvents wc As New Net.WebClient

    ' Contains file names only, no paths
    Private Files As New Queue(Of String)

    Private Sub Button1_Click(sender As Object, e As EventArgs) _
        Handles Button1.Click

        wc.Credentials = New NetworkCredential(usr, pass)

        ' Put the work in a task so UI is responsive
        Task.Run(Sub() DoUpload())
    End Sub

    Private Sub DoUpload()
        ShowProgress("", 0)

        If Files.Count > 0 Then
            Dim local As String = Files.Dequeue
            Dim FileName As String = Path.Combine(mc_dir, local)

            AppendWarning("Uploading " & FileName & "...   ")

            Try
                wc.UploadFileAsync(New Uri(info_srv & local), FileName)
            Catch ex As Exception
                AppendWarning("-ERR: Could not upload file: " & local & Environment.NewLine)
            End Try
        Else
            AppendWarning("All Uploads Finished!" & Environment.NewLine)
        End If
    End Sub

    Private Sub wc_UploadProgressChanged(sender As Object, e As UploadProgressChangedEventArgs) _
        Handles wc.UploadProgressChanged

        ' Do not use e.ProgressPercentage - it's inaccurate by half by design per Microsoft
        With String.Format("{0} KB / {1} KB", Int(e.BytesSent / 1024).ToString("N0"), Int(e.TotalBytesToSend / 1024).ToString("N0"))
            ShowProgress(.ToString, Int(e.BytesSent / e.TotalBytesToSend * 100))
        End With

    End Sub

    Private Sub wc_UploadFileCompleted(sender As Object, e As UploadFileCompletedEventArgs) _
        Handles wc.UploadFileCompleted

        Select Case True
            Case e.Cancelled
                MessageBox.Show("Cancelled")
            Case e.Error IsNot Nothing
                MessageBox.Show(e.Error.Message)
            Case Else
                AppendWarning("Upload Complete!" & Environment.NewLine)

                ' I needed this just so I could see it work, otherwise too fast
                Threading.Thread.Sleep(500)

                DoUpload()
        End Select
    End Sub

    Private Sub AppendWarning(ByVal Text As String)
        If Me.InvokeRequired Then
            Me.Invoke(Sub() AppendWarning(Text))
        Else
            tb_warnings.AppendText(Text)
        End If
    End Sub

    Private Sub ShowProgress(LabelText As String, Progress As Integer)
        If Me.InvokeRequired Then
            Me.Invoke(Sub() ShowProgress(LabelText, Progress))
        Else
            Me.lbl_progress.Text = LabelText
            Me.lbl_progress.Refresh()

            Me.ProgressBar1.Value = Progress
            Me.ProgressBar1.Refresh()
        End If
    End Sub
End Class
MrGadget
  • 1,258
  • 1
  • 10
  • 19
  • I suspect this may have been the low volume of files, but I will implement this and see. For comparison, the 2 files I'm using come out to 27MB between them (25.5 and 1.6). I can use an actual FTP to upload these to the host though, so I don't think there's a restriction in place. At any rate, I shall try your solution and see if it works. – smitty_werbermanjensen Feb 04 '17 at 18:43
  • Update: It still hangs. Something to do with the file volume I guess. I'm trying some other FTP libraries I've found, so ideally one of those works. – smitty_werbermanjensen Feb 04 '17 at 20:26
  • I just ran the above against a set of 28 files that are all over 2MB, a dozen of which were over 10MB, and 4 of those ranging 25MB to 37MB. They all uploaded fine, file sizes match, random selection open correctly. I saw an occasional stutter in the progress bar where it would hesitate about half way and jump to complete. Is that what you're referring to, or something else? Are your uploads actually failing, or this a cosmetic issue? – MrGadget Feb 05 '17 at 00:41
  • Not that it should matter, but I'm testing against my own IIS Web / FTP service, so I can see both sides of the action. FTP Service log shows exactly what I'd expect: after credentials, I have a marching sequence of STOR, PASV, Data Channel Open & Close. – MrGadget Feb 05 '17 at 00:51
  • Interesting observation...`UploadProgressChanged` never reports `e.ProgressPercentage` above 50% until it reaches 100%. It increments steadily up to 50 and holds there until the end for all file sizes. I added another progress bar and set its value alongside the first with `Int(e.BytesSent / e.TotalBytesToSend * 100)` and that reflects much more accurately, smoothly filling all the way. – MrGadget Feb 05 '17 at 01:24
  • I suspect some of this is the opacity I'm dealing with - it's a free FTP host. However, it's not cosmetic. The upload completed has msgbox in it in the basic block portion, meaning if any file upload completed, it would trigger that box. In terms of the progress I'm seeing reported, it makes it to 96%, or the rough ratio between the two files I'm testing. It seems like it can't upload the final byte for some reason, and just hangs. – smitty_werbermanjensen Feb 06 '17 at 01:06
  • Something interesting now! I used a different host, and it hangs the same way. Which suggests an issue with the code somewhere.... :\ I guess I will have to try a different library, because my implementation is very simple, I can't think it'd be something 3 people now have overlooked. – smitty_werbermanjensen Feb 07 '17 at 03:33
  • Figured it out! I had turned on some stuff to do net tracing w/ VB. It was big and excessive. Found something leaner specifically for FTP trace, and now the file seems to upload w/o issue. Something to do with the overhead of the trace, maybe? – smitty_werbermanjensen Feb 07 '17 at 04:31
  • I guess, though it turns out nothing was broken xD – smitty_werbermanjensen Feb 07 '17 at 05:05
0

For posterity:

Check your network trace settings in the VB config. I used a really verbose catch-all config I found to do the trace, but it seems the overhead was killing the upload. I've since found a much leaner focus-on-ftp set of xml to modify this and the files now upload properly. Thank you everyone!