made file downloading and caching smarter

tries eTag and ContentDisposition headers for file name+format
catches offline and server error/url error scenarios
creates a "recent" file copy if successful, and returns that as a fallback in an error scenario, if it exists
This commit is contained in:
Bailey Eaton 2023-06-05 07:39:10 +10:00
parent 230b7338a6
commit ba0cbbd5c8

View File

@ -12,13 +12,19 @@ namespace TED.Utils
internal class FileUtilities internal class FileUtilities
{ {
/// <summary> /// <summary>
/// Downloads a file from a specified URL and saves it to a cache. /// Downloads a file from a specified URL and saves it to a local cache.
/// If the file already exists in the cache, the function returns the path to the cached file. /// If the file already exists in the cache, the function returns the path to the cached file.
/// If the file does not exist in the cache, the function downloads the file, saves it to the cache, and then returns the path to the cached file. /// If the file does not exist in the cache, the function downloads the file, saves it to the cache, and then returns the path to the cached file.
/// In the event of an error with file retrieval, the most recently used file will be returned, if it exists.
/// </summary> /// </summary>
/// <param name="url">The URL of the file to download.</param> /// <param name="url">The URL of the file to download.</param>
/// <returns>A task that represents the asynchronous operation. The task result is the path to the downloaded (or cached) file.</returns> /// <returns>A task that represents the asynchronous operation. The task result is the path to the downloaded (or cached) file.</returns>
public static async Task<string> DownloadAndCacheFileAsync(string url) public static async Task<string> DownloadAndCacheFileAsync(string url)
{
var tedDirectory = Path.Combine(Path.GetTempPath(), "TED");
var recentPath = Path.Combine(tedDirectory, "recent.png");
try
{ {
using (var client = new HttpClient() using (var client = new HttpClient()
{ {
@ -26,22 +32,48 @@ namespace TED.Utils
}) })
{ {
var response = await client.GetAsync(url); var response = await client.GetAsync(url);
try
{
response.EnsureSuccessStatusCode(); response.EnsureSuccessStatusCode();
var etag = response.Headers.ETag?.Tag.Replace("\"", string.Empty) ?? "untagged";
if (!Directory.Exists(Path.Combine(Path.GetTempPath(), "TED")))
{
Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), "TED"));
} }
var path = Path.Combine(Path.GetTempPath(), "TED", $"{etag}.png"); catch (HttpRequestException e)
if (File.Exists(path))
{ {
return path; // If we catch here, it's a URL error or server-side issue.
if (File.Exists(recentPath))
{
return recentPath;
} }
var filesToDelete = Directory.GetFiles(Path.Combine(Path.GetTempPath(), "TED"), "*.png") return string.Empty;
.Where(filePath => Path.GetFileNameWithoutExtension(filePath) != etag); }
var fileName = "ted.png";
if (response.Headers.ETag != null)
{
fileName = $"{response.Headers.ETag?.Tag.Replace("\"", string.Empty)}.png";
}
else if (response.Content.Headers.ContentDisposition != null)
{
fileName = $"{response.Content.Headers.ContentDisposition.FileName}";
}
if (!Directory.Exists(tedDirectory))
{
Directory.CreateDirectory(tedDirectory);
}
var downloadPath = Path.Combine(tedDirectory, fileName);
if (File.Exists(downloadPath))
{
return downloadPath;
}
var filesToDelete = Directory.GetFiles(tedDirectory)
.Where(filePath => Path.GetFileNameWithoutExtension(filePath) != fileName && Path.GetFileNameWithoutExtension(filePath) != "recent");
foreach (var fileToDelete in filesToDelete) foreach (var fileToDelete in filesToDelete)
{ {
File.Delete(fileToDelete); File.Delete(fileToDelete);
@ -49,13 +81,28 @@ namespace TED.Utils
using (var stream = await response.Content.ReadAsStreamAsync()) using (var stream = await response.Content.ReadAsStreamAsync())
{ {
using (var fileStream = new FileStream(path, FileMode.CreateNew)) using (var fileStream = new FileStream(downloadPath, FileMode.CreateNew))
{ {
await stream.CopyToAsync(fileStream); await stream.CopyToAsync(fileStream);
return path; if(File.Exists(recentPath))
{
File.Delete(recentPath);
}
File.Copy(downloadPath, recentPath);
return downloadPath;
} }
} }
} }
} catch(HttpRequestException e)
{
// If we catch here, we're offline.
if (File.Exists(recentPath))
{
return recentPath;
}
return string.Empty;
}
} }
/// <summary> /// <summary>