119 lines
4.6 KiB
C#
119 lines
4.6 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Net.Http;
|
|
using System.Net.Sockets;
|
|
using System.Security.Cryptography;
|
|
using System.Threading;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace GenerationTasks
|
|
{
|
|
internal static class DownloadFileCore
|
|
{
|
|
public static async Task DownloadAsync(
|
|
string uri,
|
|
string destinationPath,
|
|
HttpClient httpClient,
|
|
ILogWrapper log,
|
|
CancellationToken cancellationToken,
|
|
int timeoutSeconds)
|
|
{
|
|
// Timeout if the response has not begun within 1 minute
|
|
httpClient.Timeout = TimeSpan.FromMinutes(1);
|
|
|
|
var destinationExists = File.Exists(destinationPath);
|
|
var reachedCopy = false;
|
|
try
|
|
{
|
|
using (var response = await httpClient.GetAsync(uri, cancellationToken))
|
|
{
|
|
response.EnsureSuccessStatusCode();
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
using (var responseStreamTask = response.Content.ReadAsStreamAsync())
|
|
{
|
|
var finished = await Task.WhenAny(
|
|
responseStreamTask,
|
|
Task.Delay(TimeSpan.FromSeconds(timeoutSeconds)));
|
|
|
|
if (!ReferenceEquals(responseStreamTask, finished))
|
|
{
|
|
throw new TimeoutException($"Download failed to complete in {timeoutSeconds} seconds.");
|
|
}
|
|
|
|
var responseStream = await responseStreamTask;
|
|
if (destinationExists)
|
|
{
|
|
// Check hashes before using the downloaded information.
|
|
var downloadHash = GetHash(responseStream);
|
|
responseStream.Position = 0L;
|
|
|
|
byte[] destinationHash;
|
|
using (var destinationStream = File.OpenRead(destinationPath))
|
|
{
|
|
destinationHash = GetHash(destinationStream);
|
|
}
|
|
|
|
var sameHashes = downloadHash.LongLength == destinationHash.LongLength;
|
|
for (var i = 0L; sameHashes && i < downloadHash.LongLength; i++)
|
|
{
|
|
sameHashes = downloadHash[i] == destinationHash[i];
|
|
}
|
|
|
|
if (sameHashes)
|
|
{
|
|
log.LogInformational($"Not overwriting existing and matching file '{destinationPath}'.");
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// May need to create directory to hold the file.
|
|
var destinationDirectory = Path.GetDirectoryName(destinationPath);
|
|
if (!(string.IsNullOrEmpty(destinationDirectory) || Directory.Exists(destinationDirectory)))
|
|
{
|
|
Directory.CreateDirectory(destinationDirectory);
|
|
}
|
|
}
|
|
|
|
// Create or overwrite the destination file.
|
|
reachedCopy = true;
|
|
using (var outStream = File.Create(destinationPath))
|
|
{
|
|
responseStream.CopyTo(outStream);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (HttpRequestException ex) when (destinationExists)
|
|
{
|
|
if (ex.InnerException is SocketException socketException)
|
|
{
|
|
log.LogWarning($"Unable to download {uri}, socket error code '{socketException.SocketErrorCode}'.");
|
|
}
|
|
else
|
|
{
|
|
log.LogWarning($"Unable to download {uri}: {ex.Message}");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
log.LogError($"Downloading '{uri}' failed.");
|
|
log.LogError(ex, showStackTrace: true);
|
|
if (reachedCopy)
|
|
{
|
|
File.Delete(destinationPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static byte[] GetHash(Stream stream)
|
|
{
|
|
using (var algorithm = SHA256.Create())
|
|
{
|
|
return algorithm.ComputeHash(stream);
|
|
}
|
|
}
|
|
}
|
|
}
|