16

A am creating a file upload app for Android and iOS using Xamarin PCL and i have managed to implement file upload and some sort of progress bar, but it is not working properly.

I saw some answers on stack overflow for displaying download progress, but i want to notify my users about upload progress and did not find any solution.

Here is my code:

public static async Task<string> PostFileAsync (Stream filestream, string filename, int filesize) {
        var progress = new System.Net.Http.Handlers.ProgressMessageHandler ();

        //Progress tracking
        progress.HttpSendProgress += (object sender, System.Net.Http.Handlers.HttpProgressEventArgs e) => {
            int progressPercentage = (int)(e.BytesTransferred*100/filesize);
            //Raise an event that is used to update the UI
            UploadProgressMade(sender, new System.Net.Http.Handlers.HttpProgressEventArgs(progressPercentage, null, e.BytesTransferred, null));
        };

        using (var client = HttpClientFactory.Create(progress)) {
            using (var content = new MultipartFormDataContent ("------" + DateTime.Now.Ticks.ToString ("x"))) {
                content.Add (new StreamContent (filestream), "Filedata", filename);
                using (var message = await client.PostAsync ("http://MyUrl.example", content)) {
                    var result = await message.Content.ReadAsStringAsync ();
                    System.Diagnostics.Debug.WriteLine ("Upload done");
                    return result;
                }
            }
        }
    }

Some sort of progress is displayed, but when the progress reaches 100%, the file is not uploaded yet. Message "Upload done" is also printed some time after i have received the last progress message.

Maybe the progress is displaying bytes sent out of the device and not already uploaded bytes, so when it says, that it is 100%, all of the bytes are just sent out but not yet received by the server?

Edit: Tried this solution: https://forums.xamarin.com/discussion/56716/plans-to-add-webclient-to-pcl and it works a bit better.

Artūrs Eimanis
  • 578
  • 3
  • 5
  • 22

2 Answers2

8

Simplest way to upload file with progress

You can get accurate progress by tracking the Position of the FileStream of the file that you are going to upload.

This demonstrates how to do that.

FileStream fileToUpload = File.OpenRead(@"C:\test.mp3");

HttpContent content = new StreamContent(fileToUpload);
HttpRequestMessage msg = new HttpRequestMessage{
    Content=content,
    RequestUri = new Uri(--yourUploadURL--)
}

bool keepTracking = true; //to start and stop the tracking thread
new Task(new Action(() => { progressTracker(fileToUpload, ref keepTracking); })).Start();
var result = httpClient.SendAsync(msg).Result;
keepTracking = false; //stops the tracking thread

And progressTracker() function is defined as

void progressTracker(FileStream streamToTrack, ref bool keepTracking)
{
    int prevPos = -1;
    while (keepTracking)
    {
        int pos = (int)Math.Round(100 * (streamToTrack.Position / (double)streamToTrack.Length));
        if (pos != prevPos)
        {
            Console.WriteLine(pos + "%");

        }
        prevPos = pos;

        Thread.Sleep(100); //update every 100ms
    }
}
Abraham
  • 12,140
  • 4
  • 56
  • 92
  • 2
    +1 very simple indeed! Just one extra detail if `httpClient.SendAsync(msg).Result` throws an exception (e.g. because the upload failed due to a network error) then `keepTracking` will never be set to `false` and the `progressTracker()` function will loop forever! – Eric Mutta Feb 21 '22 at 01:05
  • 1
    For future readers: this is easily solved by wrapping `SendAsync` in a `try/finally` that sets `keepTracking` to false in the `finally` block – Gillespie Aug 07 '22 at 03:51
  • 1
    Doesn't work in Blazor, as the file stream position hits 100% immediately. – Peter Morris Aug 19 '22 at 10:19
-2

This is because you are doing the math wrong.

Change : int progressPercentage = (int)(e.BytesTransferred*100/filesize);

To : int progressPercentage = (int)(e.BytesTransferred/filesize) *100;

use this code instead:

    double bytesOut = double.Parse(e.BytesTransferred.ToString());
        double totalBytes = double.Parse(filesize.ToString());
        double percentage = bytesOut / totalBytes * 100;

or you can simply use e.ProgressPercentage

Ahmed Mujtaba
  • 2,110
  • 5
  • 36
  • 67
  • Nope, that's not the problem. It was because the data was getting buffered and only sent at the end. I ended up using `HttpWebRequest` with `SendChunked` set to true and `AllowWriteStreamBuffering` set to false, then manually writing the data in the request stream. – Artūrs Eimanis Apr 27 '16 at 10:08
  • @MaximV.Pavlov As this is an old issue, i don't remember how i solved this issue, but judging by the edit on my initial problem, i think i might have used the code from the Xamarin forum link. – Artūrs Eimanis Mar 15 '17 at 10:39