locked
How to work with custom Progressable stream content RRS feed

  • Question

  • User393112 posted

    Hello according to : https://gist.github.com/HirbodBehnam/272aa5e4b82c2fb05583d095f2224861

    I implemented this :

    My call :

    `public async Task UploadFiles(FileInfo fileInfo) { string res = null;

        using (var client = new HttpClient())
        using (var multiForm = new MultipartFormDataContent())
        {
            client.Timeout = TimeSpan.FromMinutes(5); // You may need this if you are uploading a big file
    
            var file = new ProgressableStreamContent(new StreamContent(File.OpenRead(fileInfo.FullName))
                , (sent, total) => {
                    //Console.SetCursorPosition(1, 0); // Remove last line
                    Console.WriteLine("\bUploading " + ((float)sent / total) * 100f);
                });
    
            multiForm.Add(file, fileInfo.Name, fileInfo.Name); // Add the file
    
            var uploadServiceBaseAdress = "http://10.0.2.2:44560/PostFiles/";
    
            var response = await client.PostAsync(uploadServiceBaseAdress, multiForm);
            Console.WriteLine(response.StatusCode);
            if (response.StatusCode == HttpStatusCode.OK)
            {
                res = await response.Content.ReadAsStringAsync();
                Console.WriteLine(res);
    
            }
    
            return res;
        }`
    

    My class : Progressable stream content

    `internal class ProgressableStreamContent:HttpContent { ///

    /// Lets keep buffer of 20kb /// private const int defaultBufferSize = 5 * 4096;

    private HttpContent content;
    private int bufferSize;
    //private bool contentConsumed;
    private Action<long, long> progress;
    
    public ProgressableStreamContent(HttpContent content, Action<long, long> progress) : this(content, defaultBufferSize, progress) { }
    
    public ProgressableStreamContent(HttpContent content, int bufferSize, Action<long, long> progress)
    {
        if (content == null)
        {
            throw new ArgumentNullException("content");
        }
        if (bufferSize <= 0)
        {
            throw new ArgumentOutOfRangeException("bufferSize");
        }
    
        this.content = content;
        this.bufferSize = bufferSize;
        this.progress = progress;
    
        foreach (var h in content.Headers)
        {
            this.Headers.Add(h.Key, h.Value);
        }
    }
    
    protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
    
        return Task.Run(async () =>
        {
            var buffer = new Byte[this.bufferSize];
            long size;
            TryComputeLength(out size);
            var uploaded = 0;
    
    
            using (var sinput = await content.ReadAsStreamAsync())
            {
                while (true)
                {
                    var length = sinput.Read(buffer, 0, buffer.Length);
                    if (length <= 0) break;
    
                    //downloader.Uploaded = uploaded += length;
                    uploaded += length;
                    progress?.Invoke(uploaded, size);
    
                    //System.Diagnostics.Debug.WriteLine($"Bytes sent {uploaded} of {size}");
    
                    stream.Write(buffer, 0, length);
                    stream.Flush();
                }
            }
            stream.Flush();
        });
    }
    
    protected override bool TryComputeLength(out long length)
    {
        length = content.Headers.ContentLength.GetValueOrDefault();
        return true;
    }
    
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            content.Dispose();
        }
        base.Dispose(disposing);
    }
    

    }`

    It bind in the constructor of ProgressableStreamContent. Then going back to my upload file method but when it come to return it crash : Operation not supported on this platform.

    I think there is something that I dont understand and I didn't found any docs related to this. So could you explain me whats wrong ?

    Sunday, March 22, 2020 10:34 PM

Answers

  • User382871 posted

    Please test the code to check the code, here is a similalr issue you can refer to: https://forums.xamarin.com/discussion/comment/199958/#Comment_199958

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Tuesday, March 24, 2020 7:04 AM
  • User393112 posted

    I give up this working solution but not the best in my case for this the first option, with an custom http content :

    My View :

    `public partial class SomeView : ContentPage
    {
    
    public SomeView()
    {
        InitializeComponent();
    
        //YOUR CONSTRUCTOR CODE 
    
    }
    
    async void StartUploadHandler(object sender, System.EventArgs e)
    {
        styledProgressBar.Progress = 0;
    
        var fileInfo = (FileInfo)FilesList.SelectedItem;
    
        Progress<UploadBytesProgress> progressReporter = new Progress<UploadBytesProgress>();
    
        progressReporter.ProgressChanged += (s, args) => UpdateProgress((double)(100 * args.PercentComplete) / 100);
    
        fileEndpoint.UploadFiles(fileInfo, progressReporter);
    }
    
    void UpdateProgress(double obj)
    {
        styledProgressBar.Progress = obj;
    }
    }`
    

    The Custom HttpContent :

    `internal class ProgressableStreamContent:HttpContent
    {
    /// <summary>
    /// Lets keep buffer of 20kb
    /// </summary>
    private const int defaultBufferSize = 5 * 4096;
    
    private HttpContent content;
    private int bufferSize;
    //private bool contentConsumed;
    private Action<long, long> progress;
    
    public ProgressableStreamContent(HttpContent content, Action<long, long> progress) : this(content, defaultBufferSize, progress) { }
    
    public ProgressableStreamContent(HttpContent content, int bufferSize, Action<long, long> progress)
    {
        if (content == null)
        {
            throw new ArgumentNullException("content");
        }
        if (bufferSize <= 0)
        {
            throw new ArgumentOutOfRangeException("bufferSize");
        }
    
        this.content = content;
        this.bufferSize = bufferSize;
        this.progress = progress;
    
        foreach (var h in content.Headers)
        {
            this.Headers.Add(h.Key, h.Value);
        }
    }
    
    protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
    
        return Task.Run(async () =>
        {
            var buffer = new Byte[this.bufferSize];
            long size;
            TryComputeLength(out size);
            var uploaded = 0;
    
    
            using (var sinput = await content.ReadAsStreamAsync())
            {
                while (true)
                {
                    var length = sinput.Read(buffer, 0, buffer.Length);
                    if (length <= 0) break;
    
                    //downloader.Uploaded = uploaded += length;
                    uploaded += length;
                    progress?.Invoke(uploaded, size);
    
                    //System.Diagnostics.Debug.WriteLine($"Bytes sent {uploaded} of {size}");
    
                    stream.Write(buffer, 0, length);
                    stream.Flush();
                }
            }
            stream.Flush();
        });
    
    
    
    }
    
    protected override bool TryComputeLength(out long length)
    {
        length = content.Headers.ContentLength.GetValueOrDefault();
        return true;
    }
    
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            content.Dispose();
        }
        base.Dispose(disposing);
    }
    }`
    

    The Custom Upload Progress Model for the reporter :

    `public class UploadBytesProgress
    {
    public UploadBytesProgress(string fileName, int bytesSended, int totalBytes)
    {
        Filename = fileName;
        BytesSended = bytesSended;
        TotalBytes = totalBytes;
    }
    
    public int TotalBytes { get; private set; }
    
    public int BytesSended { get; private set; }
    
    public float PercentComplete { get { return (float)BytesSended / TotalBytes; } }
    
    public string Filename { get; private set; }
    
    public bool IsFinished { get { return BytesSended == TotalBytes; } }
    }`
    

    And The http call :

    `public async Task<string> UploadFiles(FileInfo fileInfo, IProgress<UploadBytesProgress> progessReporter)
        {
        string res = null;
        IAndroidFileHelper androidFileHelper = DependencyService.Get<IAndroidFileHelper>();
    
        using (var client = new HttpClient())
        {
            using (var multiForm = new MultipartFormDataContent())
            {
                var bytesFile = androidFileHelper.LoadLocalFile(fileInfo.FullName);
                ByteArrayContent byteArrayContent = new ByteArrayContent(bytesFile);
    
                multiForm.Add(byteArrayContent, fileInfo.Name, fileInfo.Name);
    
                var progressContent = new ProgressableStreamContent(multiForm, 4096, (sent, total) =>
                {
                    UploadBytesProgress args = new UploadBytesProgress("SERVEUR URL FOR UPLOAD", (int)sent, (int)total);
                    progessReporter.Report(args);
                });
    
    
                var uploadServiceBaseAdress = "SERVEUR URL FOR UPLOAD";
    
                var response = await client.PostAsync(uploadServiceBaseAdress, progressContent);
                Console.WriteLine(response.StatusCode);
    
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    res = await response.Content.ReadAsStringAsync();
                    Console.WriteLine(res);
    
                }
                else
                {
                    throw new Exception(response.ReasonPhrase);
                }
    
                return res;
            }
        }
     }`
    

    More info about the custom httpContent and the SerializeToStreamAsync method here : https://stackoverflow.com/questions/20493197/posting-a-custom-type-with-httpclient

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Tuesday, March 24, 2020 8:43 PM

All replies

  • User382871 posted

    but when it come to return it crash : Operation not supported on this platform. 1.The custom HttpContent doesn't implement native implementations,make sure you've installed Microsoft.Net.Http Nuget Package.

    2.Try to display the download progress with the ProgressBar widget in Android. Add the asynchronous event handler assigned to the uploading button's Click event: ``` async void StartUploadHandler(object sender, System.EventArgs e) { _progressBar.Progress = 0; Progress progressReporter = new Progress(); progressReporter.ProgressChanged += (s, args) => _progressBar.Progress = (int)(100 * args.PercentComplete);

    Task<int> uploadTask = DownloadHelper.CreateUploadTask(UploaHelper.ImageToUpload, progressReporter);
    int bytesUpload = await uploadTask;
    System.Diagnostics.Debug.WriteLine("Uploa {0} bytes.", bytesUpload);
    

    } ```

    Check the tutorial: https://github.com/xamarin/docs-archive/tree/master/Recipes/cross-platform/networking/download_progress

    Monday, March 23, 2020 3:10 AM
  • User393112 posted

    Ty ! I will check this soon

    Monday, March 23, 2020 9:06 AM
  • User393112 posted

    There is something that I dont understand :

    Here is the request for Download a file :

     `using (var stream = await client.OpenReadTaskAsync(urlToUpload))
            {
                byte[] buffer = new byte[BufferSize];
                totalBytes = Int32.Parse(client.ResponseHeaders[HttpResponseHeader.ContentLength]);
    
                for (; ; )
                {
                    int bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);
                    if (bytesRead == 0)
                    {
                        await Task.Yield();
                        break;
                    }
    
                    sendedBytes += bytesRead;
                    if (progessReporter != null)
                    {
                        UploadBytesProgress args = new UploadBytesProgress(urlToUpload, sendedBytes, totalBytes);
                        progessReporter.Report(args);
                    }
                }
            }`
    

    But how do I interpreter the quantity of bytes uploaded ?

    Monday, March 23, 2020 3:05 PM
  • User393112 posted

    `public class UploadHelper { public static readonly int BufferSize = 4096; public static int sendedBytes = 0;

        public static async Task<int> CreateUploadTask(Uri urlToUpload, IProgress<UploadBytesProgress> progessReporter, string fileName)
        {
    
            int totalBytes = 0;
    
            WebClient client = new WebClient();
    
            client.Headers.Add("content-Type", "application/pdf");
    
            client.UploadProgressChanged += Client_UploadProgressChanged;
            client.UploadFileCompleted += Client_UploadFileCompleted;
    
            client.UploadFileAsync(urlToUpload, "POST", fileName);
    
            client.UploadProgressChanged -= Client_UploadProgressChanged;
            client.UploadFileCompleted -= Client_UploadFileCompleted;
    
            return sendedBytes;
        }
    
        private static void Client_UploadFileCompleted(object sender, UploadFileCompletedEventArgs e)
        {
    
        }
    
        private static void Client_UploadProgressChanged(object sender, UploadProgressChangedEventArgs e)
        {
            sendedBytes = (int)e.BytesSent; 
        }
    }`
    

    Here an example what do you think about this ?

    Monday, March 23, 2020 3:26 PM
  • User382871 posted

    Please test the code to check the code, here is a similalr issue you can refer to: https://forums.xamarin.com/discussion/comment/199958/#Comment_199958

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Tuesday, March 24, 2020 7:04 AM
  • User393112 posted

    According to your link and the offical doc : https://docs.microsoft.com/fr-fr/dotnet/api/system.net.webclient.uploadprogresschanged?view=netframework-4.8

    I made this :

    `void StartUploadHandler(object sender, System.EventArgs e)
        {
            this.fileInfo = (FileInfo)FilesList.SelectedItem;
    
            Upload(fileInfo);
    
        }
    
        void Upload(FileInfo fileInfo)
        {
    
            WebClient client = new WebClient();
            Uri uri = new Uri("http://10.0.2.2:44560/PostFilesForUncompressDocs/");
    
            client.UploadFileCompleted += new UploadFileCompletedEventHandler(UploadFileCallback2);
            client.UploadProgressChanged += new UploadProgressChangedEventHandler(UploadProgressCallback);
    
            byte[] bytes = File.ReadAllBytes(fileInfo.FullName);
    
            client.UploadDataTaskAsync(uri.ToString(), "POST", bytes);
    
            Debug.WriteLine("File upload started.");
        }
    
        void UploadProgressCallback(object sender, UploadProgressChangedEventArgs e)
        {
            // Displays the operation identifier, and the transfer progress.
            double progress = (double)e.BytesSent / fileInfo.Length;
            double progressPercentage =(double) progress * 100;
    
            //ALWAIS EQUAL TO -1
            Debug.WriteLine(e.TotalBytesToSend);
            //ALWAIS EQUAL TO 0
            Debug.WriteLine(e.ProgressPercentage);
        }
    
        void UploadFileCallback2(object sender, UploadFileCompletedEventArgs e)
        {
            Debug.WriteLine("succes");
        }`
    

    But now new problems appear, I dont have the e.progressPercentage from the UploadProgressChangedEventArgs is alwais equal to 0. Its look like the web client don't found the lenght of the request send. I checked in my controller side and the size request was there.

    It's wear

    Tuesday, March 24, 2020 12:41 PM
  • User393112 posted

    I give up this working solution but not the best in my case for this the first option, with an custom http content :

    My View :

    `public partial class SomeView : ContentPage
    {
    
    public SomeView()
    {
        InitializeComponent();
    
        //YOUR CONSTRUCTOR CODE 
    
    }
    
    async void StartUploadHandler(object sender, System.EventArgs e)
    {
        styledProgressBar.Progress = 0;
    
        var fileInfo = (FileInfo)FilesList.SelectedItem;
    
        Progress<UploadBytesProgress> progressReporter = new Progress<UploadBytesProgress>();
    
        progressReporter.ProgressChanged += (s, args) => UpdateProgress((double)(100 * args.PercentComplete) / 100);
    
        fileEndpoint.UploadFiles(fileInfo, progressReporter);
    }
    
    void UpdateProgress(double obj)
    {
        styledProgressBar.Progress = obj;
    }
    }`
    

    The Custom HttpContent :

    `internal class ProgressableStreamContent:HttpContent
    {
    /// <summary>
    /// Lets keep buffer of 20kb
    /// </summary>
    private const int defaultBufferSize = 5 * 4096;
    
    private HttpContent content;
    private int bufferSize;
    //private bool contentConsumed;
    private Action<long, long> progress;
    
    public ProgressableStreamContent(HttpContent content, Action<long, long> progress) : this(content, defaultBufferSize, progress) { }
    
    public ProgressableStreamContent(HttpContent content, int bufferSize, Action<long, long> progress)
    {
        if (content == null)
        {
            throw new ArgumentNullException("content");
        }
        if (bufferSize <= 0)
        {
            throw new ArgumentOutOfRangeException("bufferSize");
        }
    
        this.content = content;
        this.bufferSize = bufferSize;
        this.progress = progress;
    
        foreach (var h in content.Headers)
        {
            this.Headers.Add(h.Key, h.Value);
        }
    }
    
    protected override Task SerializeToStreamAsync(Stream stream, TransportContext context)
    {
    
        return Task.Run(async () =>
        {
            var buffer = new Byte[this.bufferSize];
            long size;
            TryComputeLength(out size);
            var uploaded = 0;
    
    
            using (var sinput = await content.ReadAsStreamAsync())
            {
                while (true)
                {
                    var length = sinput.Read(buffer, 0, buffer.Length);
                    if (length <= 0) break;
    
                    //downloader.Uploaded = uploaded += length;
                    uploaded += length;
                    progress?.Invoke(uploaded, size);
    
                    //System.Diagnostics.Debug.WriteLine($"Bytes sent {uploaded} of {size}");
    
                    stream.Write(buffer, 0, length);
                    stream.Flush();
                }
            }
            stream.Flush();
        });
    
    
    
    }
    
    protected override bool TryComputeLength(out long length)
    {
        length = content.Headers.ContentLength.GetValueOrDefault();
        return true;
    }
    
    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            content.Dispose();
        }
        base.Dispose(disposing);
    }
    }`
    

    The Custom Upload Progress Model for the reporter :

    `public class UploadBytesProgress
    {
    public UploadBytesProgress(string fileName, int bytesSended, int totalBytes)
    {
        Filename = fileName;
        BytesSended = bytesSended;
        TotalBytes = totalBytes;
    }
    
    public int TotalBytes { get; private set; }
    
    public int BytesSended { get; private set; }
    
    public float PercentComplete { get { return (float)BytesSended / TotalBytes; } }
    
    public string Filename { get; private set; }
    
    public bool IsFinished { get { return BytesSended == TotalBytes; } }
    }`
    

    And The http call :

    `public async Task<string> UploadFiles(FileInfo fileInfo, IProgress<UploadBytesProgress> progessReporter)
        {
        string res = null;
        IAndroidFileHelper androidFileHelper = DependencyService.Get<IAndroidFileHelper>();
    
        using (var client = new HttpClient())
        {
            using (var multiForm = new MultipartFormDataContent())
            {
                var bytesFile = androidFileHelper.LoadLocalFile(fileInfo.FullName);
                ByteArrayContent byteArrayContent = new ByteArrayContent(bytesFile);
    
                multiForm.Add(byteArrayContent, fileInfo.Name, fileInfo.Name);
    
                var progressContent = new ProgressableStreamContent(multiForm, 4096, (sent, total) =>
                {
                    UploadBytesProgress args = new UploadBytesProgress("SERVEUR URL FOR UPLOAD", (int)sent, (int)total);
                    progessReporter.Report(args);
                });
    
    
                var uploadServiceBaseAdress = "SERVEUR URL FOR UPLOAD";
    
                var response = await client.PostAsync(uploadServiceBaseAdress, progressContent);
                Console.WriteLine(response.StatusCode);
    
                if (response.StatusCode == HttpStatusCode.OK)
                {
                    res = await response.Content.ReadAsStringAsync();
                    Console.WriteLine(res);
    
                }
                else
                {
                    throw new Exception(response.ReasonPhrase);
                }
    
                return res;
            }
        }
     }`
    

    More info about the custom httpContent and the SerializeToStreamAsync method here : https://stackoverflow.com/questions/20493197/posting-a-custom-type-with-httpclient

    • Marked as answer by Anonymous Thursday, June 3, 2021 12:00 AM
    Tuesday, March 24, 2020 8:43 PM