Search code examples
c#file-uploadhttpwebrequest

tracking upload progress of multiple file uploads using multipart/body web request


I'm using HttpWebRequest to upload files to the server. The request sends 2 files to the server, a video file and an image file. I'm trying to track the progress of entire progress but the issue is, the progress log runs separately for each file upload. I want the progress to show only once for both the uploads but I can't quite figure out how to do it. Here's my client side code:

Dictionary<string, string> fields = new Dictionary<string, string>();
        fields.Add("username", username);

        HttpWebRequest hr = WebRequest.Create(url) as HttpWebRequest;
        hr.Timeout = 500000;
        string bound = "----------------------------" + DateTime.Now.Ticks.ToString("x");
        hr.ContentType = "multipart/form-data; boundary=" + bound;
        hr.Method = "POST";
        hr.KeepAlive = true;
        hr.Credentials = CredentialCache.DefaultCredentials;

        byte[] boundBytes = Encoding.ASCII.GetBytes("\r\n--" + bound + "\r\n");
        string formDataTemplate = "\r\n--" + bound + "\r\nContent-Disposition: form-data; name=\"{0}\";\r\n\r\n{1}";

        Stream s = hr.GetRequestStreamWithTimeout(1000000);

        foreach (string key in fields.Keys)
        {
            byte[] formItemBytes = Encoding.UTF8.GetBytes(
                string.Format(formDataTemplate, key, fields[key]));
            s.Write(formItemBytes, 0, formItemBytes.Length);
        }

        s.Write(boundBytes, 0, boundBytes.Length);

        string headerTemplate =
            "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"\r\n Content-Type: application/octet-stream\r\n\r\n";

        List<string> files = new List<string> { fileUrl, thumbUrl };
        List<string> type = new List<string> { "video", "thumb" };

        int count = 0;
        foreach (string f in files)
        {
            var m = Path.GetFileName(f);
            var t = type[count];
            var j = string.Format(headerTemplate, t, m);
            byte[] headerBytes = Encoding.UTF8.GetBytes(
                string.Format(headerTemplate, type[count], Path.GetFileName(f)));

            s.Write(headerBytes, 0, headerBytes.Length);
            FileStream fs = new FileStream(f, FileMode.Open, FileAccess.Read);
            int bytesRead = 0;
            long bytesSoFar = 0;
            byte[] buffer = new byte[1024];
            while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) != 0)
            {
                bytesSoFar += bytesRead;
                s.Write(buffer, 0, buffer.Length);
                Console.WriteLine(string.Format("sending file data {0:0.000}%", (bytesSoFar * 100.0f) / fs.Length));

            }

            s.Write(boundBytes, 0, boundBytes.Length);
            fs.Close();

            count += 1;
        }

        s.Close();

        string respString = "";
        hr.BeginGetResponse((IAsyncResult res) =>
        {
            WebResponse resp = ((HttpWebRequest)res.AsyncState).EndGetResponse(res);

            StreamReader respReader = new StreamReader(resp.GetResponseStream());
            respString = respReader.ReadToEnd();
            resp.Close();
            resp = null;
        }, hr);

        while (!hr.HaveResponse)
        {
            Console.Write("hiya bob!");
            Thread.Sleep(150);
        }

        Console.Write(respString);
        hr = null;

How do I combine the progress log for both uploads into a single log? Any help is appreciated.


Solution

  • One option is to calculate the total number of bytes you need to send before doing any work:

    // Calculate the total size to upload before starting work
    long totalToUpload = 0;
    foreach (var f in files)
    {
        totalToUpload += (new FileInfo(f)).Length;
    }
    

    Then keep track of the total number of bytes sent in any file, and use that in your calculation of progress:

    int count = 0;
    long bytesSoFar = 0;
    
    foreach (string f in files)
    {
        // ... Your existing work ...
    
        while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) != 0)
        {
            bytesSoFar += bytesRead;
            // Make sure to only write the number of bytes read from the file
            s.Write(buffer, 0, bytesRead);
            // Console.WriteLine takes a string.Format() style string
            Console.WriteLine("sending file data {0:0.000}%", (bytesSoFar * 100.0f) / totalToUpload);
        }