0

So I'm trying to download two images and just for demonstration I've checked their sizes and summed it up to a variable called totalBytes.

I want to keep track of how much has been downloaded out of those total bytes so I can calculate the percentage by taking downloaded / totalBytes * 100

But I have no idea how to keep track of the mount of bytes that has been downloaded.

public static int totalBytes = 1378954;
static void Main(string[] args)
{
    var images = new List<string>
    {
        "http://4.bp.blogspot.com/-HTvSYzA-pO4/UgQb4Zh_u0I/AAAAAAAAEuI/XwhtogT_1tA/s1600/3+cute2.jpg",
        "http://getwallpapers.com/wallpaper/full/7/7/0/74728.jpg"
    };
    foreach (var image in images)
    {
        int i = 0;
        using (var wc = new WebClient())
        {
            wc.DownloadProgressChanged += Wc_DownloadProgressChanged;
            wc.DownloadFileAsync(new Uri(image), $"image{i}.png");
            i++;

            Console.ReadKey();
        }

    }
}

private static void Wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
    Console.WriteLine($"Downloaded: {e.BytesReceived} out of {totalBytes}");
}
Eugene Podskal
  • 10,270
  • 5
  • 31
  • 53
Mark Denom
  • 987
  • 1
  • 8
  • 24
  • What is wrong with this code? It should work and write received bytes to console. If you need total bytes received for both images keep a reference to them (put received size of images in an array) and whenever `Wc_DownloadProgressChanged` is fired use new data to recalculate total received bytes. – Xaqron Aug 01 '19 at 18:49
  • I'm not quite sure what you mean, I do want to keep track of the total amount received. – Mark Denom Aug 01 '19 at 18:51
  • Just use a separate field `private static int _bytesReceived = 0` and add to it `e.BytesReceived` inside the `Wc_DownloadProgressChanged` method as it has been already said above. And, probably, at least add a `volatile` specifier to that `_bytesReceived` field to avoid possible concurrency issues. – Eugene Podskal Aug 01 '19 at 18:57
  • That doesn't work, `_bytesReceived ` gets way bigger than the total value – Mark Denom Aug 01 '19 at 18:59
  • Oh, yes. Sorry - didn't check the [docs](https://learn.microsoft.com/en-Us/dotnet/api/system.net.downloadprogresschangedeventargs?view=netframework-4.8) that it is actually more like `TotalBytesReceived` (well, good naming is important...). Also, the `volatile` part was completely wrong (I wanted to say https://learn.microsoft.com/en-us/dotnet/api/system.threading.interlocked.add?view=netframework-4.8 but somehow got sidetracked). – Eugene Podskal Aug 02 '19 at 05:01

1 Answers1

0

Assuming that you do not intend to actually start downloading files in parallel (as you dispose the WebClient before starting another download), the problem can be solved with two field

  1. One to store the current total
  2. Another to store the previous ReceivedByte value

    private static long _totalBytesReceivedAllFiles = 0;
    
    private static long _previousBytesReceivedForCurrentFile = 0;
    
    private static object _lock = new Object();
    
    private static void Wc_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
    {
        // There are no guarantees in the documentation that such events are serialized and can't execute in parallel even for a single file,
        // thus we will use lock to at least partially serialize it and ensure atomic field access. 
        // !!!It is not intended to handle actual parallel downloads!!!
        lock (_lock)
        {
            _totalBytesReceivedAllFiles = _totalBytesReceivedAllFiles - _previousBytesReceivedForCurrentFile + e.BytesReceived;
            _previousBytesReceivedForCurrentFile = e.BytesReceived;
            Console.WriteLine($"Downloaded: {_totalBytesReceivedAllFiles} out of {totalBytes}");
            if (e.ProgressPercentage == 100)
            {
                Console.WriteLine("Current file downloaded");
                _previousBytesReceivedForCurrentFile = 0;
            }
        }
    }
    

If on the other hand you want to start downloads truly in parallel, then you will have to

  1. Not to dispose (in the loop!) the client, for obvious reasons.
  2. Use a per-file Wc_DownloadProgressChanged with closure to have an actual per-file _previousBytesReceivedForCurrentFile
Eugene Podskal
  • 10,270
  • 5
  • 31
  • 53