Reputation: 12027
I have the following code that monitors the upload process. I'm using e.ProgressPercentage
to keep track of the current progress, but it's returning wrong values.
using (WebClient client = new WebClient())
{
client.Credentials = new NetworkCredential(DataIO.FTP.Username, DataIO.FTP.Password);
Uri uri = new Uri($"ftp://{DataIO.FTP.Server}/{DataIO.FTP.UploadDir}/{DataIO.SelectedOutputArchive}.tar.gz");
client.UploadProgressChanged += new UploadProgressChangedEventHandler(delegate (object s2, UploadProgressChangedEventArgs e2) {
ProgressLabel.Invoke((MethodInvoker)delegate
{
ProgressLabel.Text = $"{(e2.BytesSent / 1024).ToString()} KB / {(e2.TotalBytesToSend / 1024).ToString()} KB";
});
UploadProgressBar.Invoke((MethodInvoker)delegate
{
UploadProgressBar.Value = e2.ProgressPercentage; // returns 0-50
//UploadProgressBar.Value = (int)Clamp((float)e2.ProgressPercentage / 50 * 100, 0, 100);
});
Debug.WriteLine(e2.ProgressPercentage);
});
client.UploadFileCompleted += new UploadFileCompletedEventHandler(delegate {
this.Invoke((MethodInvoker)delegate
{
this.Text = "Upload: Success";
});
Thread.Sleep(2000);
this.Invoke((MethodInvoker)delegate
{
this.Close();
});
});
client.UploadFileAsync(uri, "STOR", localFilePath);
}
I tried debugging it in the output window and the values it returned were like this:
0
0
1
0
1
0
1
1
1
2
2
1
2
2
3
3
2
3
3
...
49
49
50
49
100
49
49
These back jumps are not necessarily important but jumping from 49 to 100 is.
I assumed that it always returns between 0-50 (after realizing that it doesn't to up from 50). So I needed to scale it to 0-100. This worked, but then it jumped from 49 to 100. Scaling expects numbers from 0-50, and receiving 100 breaks it (results in overflow (200), progressbar expects 0-100). Then I had to clamp the result value. So I came up with this:
(int)Clamp((float)e2.ProgressPercentage / 50 * 100, 0, 100)
While this works fine now, I'm wondering why I need to hack my way to make it work. Am I doing something wrong?
Update: I have decided not to rely on e.ProgressPercentage
and do the calculation myself:
UploadProgressBar.Value = (int)(e2.BytesSent / (float)e2.TotalBytesToSend * 100);
Upvotes: 1
Views: 1193
Reputation: 562
This is by design. The reason is
when you do an upload the server can also send data as a response to the upload. In general this is true for any HTTP Request. Consider a form uploading a bunch of input and the server sending a whole bunch of data, perhaps an SQL server database report. In all these cases we divice the upload and download to 50% and 50%. So if the server is not sending any data then it will jump from 50 to 100 directly
Also check this thread for possible issues with this approach.
Another solution
Another option for visualizing the correct values of your upload is to use another two properties of the UploadProgressChangedEventArgs object - BytesSent, TotalBytesToSend - and make the calculation yourself. For reference, check the examples section.
EDIT: I could not find anywhere documented what is the reason of implementing this in such manner, but here is the code that is responsible for this behavior:
private void PostProgressChanged(AsyncOperation asyncOp, ProgressData progress) {
if (asyncOp != null && progress.BytesSent + progress.BytesReceived > 0)
{
int progressPercentage;
if (progress.HasUploadPhase)
{
if (progress.TotalBytesToReceive < 0 && progress.BytesReceived == 0)
{
progressPercentage = progress.TotalBytesToSend < 0 ? 0 : progress.TotalBytesToSend == 0 ? 50 : (int)((50 * progress.BytesSent) / progress.TotalBytesToSend);
}
else
{
progressPercentage = progress.TotalBytesToSend < 0 ? 50 : progress.TotalBytesToReceive == 0 ? 100 : (int) ((50 * progress.BytesReceived) / progress.TotalBytesToReceive + 50);
}
asyncOp.Post(reportUploadProgressChanged, new UploadProgressChangedEventArgs(progressPercentage, asyncOp.UserSuppliedState, progress.BytesSent, progress.TotalBytesToSend, progress.BytesReceived, progress.TotalBytesToReceive));
}
else
{
progressPercentage = progress.TotalBytesToReceive < 0 ? 0 : progress.TotalBytesToReceive == 0 ? 100 : (int) ((100 * progress.BytesReceived) / progress.TotalBytesToReceive);
asyncOp.Post(reportDownloadProgressChanged, new DownloadProgressChangedEventArgs(progressPercentage, asyncOp.UserSuppliedState, progress.BytesReceived, progress.TotalBytesToReceive));
}
}
}
Source: https://referencesource.microsoft.com/System/net/System/Net/webclient.cs.html#2966
Upvotes: 2
Reputation: 202292
I was able to reproduce the same, including the strange drop.
Consider using FtpWebRequest
from a separate thread instead. That way, you have a full control of the upload progress.
See my answer to How can we show progress bar for upload with FtpWebRequest.
Upvotes: 0