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.

0

2 Answers 2

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
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

+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!
For future readers: this is easily solved by wrapping SendAsync in a try/finally that sets keepTracking to false in the finally block
Doesn't work in Blazor, as the file stream position hits 100% immediately.
@Abraham: For me, it looks like, this solution is not thread safe. To realize this, the Stream.Synchronized method should be used for fileToUpload.
-3

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

2 Comments

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.
@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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.