WebClient allows you to download a file using Uri in synchronous and asynchronous mode.
But it became necessary to limit the download speed of the file.

Using WebClient, I did not find a way to solve this problem.
There is one link where they show how to get the download data, but it's still not that.

Okay, I did not find it okay - I thought. And I decided to look for another way to download the file and ... did not find it .

And what methods of downloading files using C #, besides using WebClient , do you know? Share, please.

And if you know how to limit the speed of downloading files - I will also be happy to see.

3 answers 3

Here is a simple solution to suspend the download if speed is exceeded.

public static async Task DownloadUriWithThrottling(Uri uri, string path, double speedKbps) { var req = WebRequest.CreateHttp(uri); using (var resp = await req.GetResponseAsync()) using (var stream = resp.GetResponseStream()) using (var outfile = File.OpenWrite(path)) { var startTime = DateTime.Now; long totalDownloaded = 0; var buffer = new byte[0x10000]; while (true) { var actuallyRead = await stream.ReadAsync(buffer, 0, buffer.Length); if (actuallyRead == 0) // end of stream return; await outfile.WriteAsync(buffer, 0, actuallyRead); totalDownloaded += actuallyRead; // recalc speed and wait var expectedTime = totalDownloaded / 1024.0 / speedKbps; var actualTime = (DateTime.Now - startTime).TotalSeconds; if (expectedTime > actualTime) await Task.Delay(TimeSpan.FromSeconds(expectedTime - actualTime)); } } } 

(Not tested.)

Perhaps at low speeds it makes sense to take the size of the buffer not from the ceiling, but by some logic.

  • one
    At low speeds with a fast connection, you need to make a fixed delay, and manage the buffer size. Those. the formula turns out: expectedDownloaded = totalTime / 1024.0 / speedKbps . - Pavel Mayorov
  • @PavelMayorov: Yeah, for this case, the buffer is needed for reasons of "per second jumps" or something like that. - VladD
  • @PavelMayorov: But this is already fine tuning. Let's see if this logic of the vehicle is suitable. (And I left for lunch.) - VladD
  • @VladD, more than, thanks. Enjoy your meal. - Ep1demic
  • @ Ep1demic: Thank you! - VladD

TCP protocol works in such a way that data over the network will be transmitted at the same speed as you read it from the network stream.

Therefore, you need to use methods that return a stream (Stream) and limit your data reading speed from there. For example, through Thread.Sleep in synchronous code or via Task.Delay in asynchronous.

  • Yeah, just such an example and sketched. - VladD

I found here such a solution with the use of delay in excess of the permissible speed

 public class ThrottledStream : Stream { #region Properties private int maxBytesPerSecond; /// <summary> /// Number of Bytes that are allowed per second /// </summary> public int MaxBytesPerSecond { get { return maxBytesPerSecond; } set { if (value < 1) throw new ArgumentException("MaxBytesPerSecond has to be >0"); maxBytesPerSecond = value; } } #endregion #region Private Members private int processed; System.Timers.Timer resettimer; AutoResetEvent wh = new AutoResetEvent(true); private Stream parent; #endregion /// <summary> /// Creates a new Stream with Databandwith cap /// </summary> /// <param name="parentStream"></param> /// <param name="maxBytesPerSecond"></param> public ThrottledStream(Stream parentStream, int maxBytesPerSecond=int.MaxValue) { MaxBytesPerSecond = maxBytesPerSecond; parent = parentStream; processed = 0; resettimer = new System.Timers.Timer(); resettimer.Interval = 1000; resettimer.Elapsed += resettimer_Elapsed; resettimer.Start(); } protected void Throttle(int bytes) { try { processed += bytes; if (processed >= maxBytesPerSecond) wh.WaitOne(); } catch { } } private void resettimer_Elapsed(object sender, ElapsedEventArgs e) { processed = 0; wh.Set(); } #region Stream-Overrides public override void Close() { resettimer.Stop(); resettimer.Close(); base.Close(); } protected override void Dispose(bool disposing) { resettimer.Dispose(); base.Dispose(disposing); } public override bool CanRead { get { return parent.CanRead; } } public override bool CanSeek { get { return parent.CanSeek; } } public override bool CanWrite { get { return parent.CanWrite; } } public override void Flush() { parent.Flush(); } public override long Length { get { return parent.Length; } } public override long Position { get { return parent.Position; } set { parent.Position = value; } } public override int Read(byte[] buffer, int offset, int count) { Throttle(count); return parent.Read(buffer, offset, count); } public override long Seek(long offset, SeekOrigin origin) { return parent.Seek(offset, origin); } public override void SetLength(long value) { parent.SetLength(value); } public override void Write(byte[] buffer, int offset, int count) { Throttle(count); parent.Write(buffer, offset, count); } #endregion } 
  • Quite an interesting solution, but there is no direct download on Uri (you need to play with stream). Let's wait for a more beautiful solution;) - Ep1demic
  • @ Ep1demic and beautiful and will not. There is no built-in functionality for WebClient. - Pavel Mayorov
  • @PavelMayorov, well, why not?) Here is an example of a shorter, more concise and with the transmitted parameter Uri, and not stream - Ep1demic
  • @ Ep1demic mda ... - Pavel Mayorov
  • @ Ep1demic and nothing that is also used inside the stream? - Pavel Mayorov