I have an application that has to download a large payload (a few hundred MB), and in some circumstances on a device with a slow connection.
So I have this:
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(activeResource.Content);
client.Timeout = TimeSpan.FromHours(6);
binary = await client.GetByteArrayAsync(activeResource.Content);
}
I've set the Timeout to be 6 hours to be safe - in realistic terms we have seem instances where it has taken up to an hour to download.
My question is: Does this Timeout specify the timeout for the entire transaction, or is it the timeout while waiting for a response?
In other words, what I want is, to allow for up to 6 hours (or thereabouts) for the entire download to complete, but if, say there is no data coming through, due to, say a dropped connection, I don't want the user to have to wait 6 hours to find out.
And if it's the former, how do I set a short timeout for waiting for a response from the server, but allow a long time for the download to complete?
What ended up doing the trick was a variation of Jeevan Abi's answer
I ditched the TimeOut property altogether and put the read into a cancellable task.
{
var downloadTimeout = TimeSpan.FromHours(6);
var inactivityTimeout = TimeSpan.FromSeconds(30);
using (var client = new HttpClient())
{
client.Timeout = Timeout.InfiniteTimeSpan;
var cancellationTokenSource = new CancellationTokenSource(downloadTimeout);
using (var response = await client.GetAsync(resource.ResourceUrl, HttpCompletionOption.ResponseHeadersRead, cancellationTokenSource.Token))
{
response.EnsureSuccessStatusCode();
using (var contentStream = await response.Content.ReadAsStreamAsync())
{
using (var fileStream = new FileStream(tempFile, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true))
{
var buffer = new byte[8192];
int bytesRead;
long totalBytesRead = 0;
while ((bytesRead = await ReadDataAsync(contentStream, buffer, 0, buffer.Length, inactivityTimeout, cancellationTokenSource.Token)) > 0)
{
await fileStream.WriteAsync(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
}
}
}
}
}
}
private async Task<int> ReadDataAsync(Stream stream, byte[] buffer, int offset, int count, TimeSpan timeout, CancellationToken cancelToken)
{
var readTask = Task.Run(() => stream.ReadAsync(buffer, offset, count), cancelToken);
if (await Task.WhenAny(readTask, Task.Delay(timeout)) == readTask)
{
return await readTask;
}
else
{
throw new TimeoutException("Connection timed out during download.");
}
}