How to display upload progress using C# HttpClient PostAsync

16,112

Solution 1

Try something like this:

I faced same issue. I fixed it by implementing custom HttpContent. I use this object to track percentage of upload progress, you can add an event to and listen it. You should customize SerializeToStreamAsync method.

internal class ProgressableStreamContent : HttpContent
{
    private const int defaultBufferSize = 4096;

    private Stream content;
    private int bufferSize;
    private bool contentConsumed;
    private Download downloader;

    public ProgressableStreamContent(Stream content, Download downloader) : this(content, defaultBufferSize, downloader) {}

    public ProgressableStreamContent(Stream content, int bufferSize, Download downloader)
    {
        if(content == null)
        {
            throw new ArgumentNullException("content");
        }
        if(bufferSize <= 0)
        {
            throw new ArgumentOutOfRangeException("bufferSize");
        }

        this.content = content;
        this.bufferSize = bufferSize;
        this.downloader = downloader;
    }

    protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
        Contract.Assert(stream != null);

        PrepareContent();

        return Task.Run(() =>
        {
            var buffer = new Byte[this.bufferSize];
            var size = content.Length;
            var uploaded = 0;

            downloader.ChangeState(DownloadState.PendingUpload);

            using(content) while(true)
            {
                var length = content.Read(buffer, 0, buffer.Length);
                if(length <= 0) break;

                downloader.Uploaded = uploaded += length;

                stream.Write(buffer, 0, length);

                downloader.ChangeState(DownloadState.Uploading);
            }

            downloader.ChangeState(DownloadState.PendingResponse);
        });
    }

    protected override bool TryComputeLength(out long length)
    {
        length = content.Length;
        return true;
    }

    protected override void Dispose(bool disposing)
    {
        if(disposing)
        {
            content.Dispose();
        }
        base.Dispose(disposing);
    }


    private void PrepareContent()
    {
        if(contentConsumed)
        {
            // If the content needs to be written to a target stream a 2nd time, then the stream must support
            // seeking (e.g. a FileStream), otherwise the stream can't be copied a second time to a target 
            // stream (e.g. a NetworkStream).
            if(content.CanSeek)
            {
                content.Position = 0;
            }
            else
            {
                throw new InvalidOperationException("SR.net_http_content_stream_already_read");
            }
        }

        contentConsumed = true;
    }
}

Refer :

Solution 2

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
    }
}
Share:
16,112

Related videos on Youtube

Artūrs Eimanis
Author by

Artūrs Eimanis

Updated on July 05, 2022

Comments

  • Artūrs Eimanis
    Artūrs Eimanis almost 2 years

    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
    Artūrs Eimanis about 8 years
    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
    Artūrs Eimanis about 7 years
    @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.
  • Demian Flavius
    Demian Flavius about 7 years
    do you have a full snippet example?
  • Vitali K
    Vitali K almost 7 years
  • MrZander
    MrZander over 4 years
    This is awesome. Thanks!
  • Eric Mutta
    Eric Mutta about 2 years
    +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!