From ba0cbbd5c818966e271663b2710e1ac884d3f80f Mon Sep 17 00:00:00 2001 From: Bailey Eaton Date: Mon, 5 Jun 2023 07:39:10 +1000 Subject: [PATCH] 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 --- src/TED/TED.Utils/FileUtilities.cs | 105 +++++++++++++++++++++-------- 1 file changed, 76 insertions(+), 29 deletions(-) diff --git a/src/TED/TED.Utils/FileUtilities.cs b/src/TED/TED.Utils/FileUtilities.cs index d9acb99..9a16e18 100644 --- a/src/TED/TED.Utils/FileUtilities.cs +++ b/src/TED/TED.Utils/FileUtilities.cs @@ -12,49 +12,96 @@ namespace TED.Utils internal class FileUtilities { /// - /// 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 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. /// /// The URL of the file to download. /// A task that represents the asynchronous operation. The task result is the path to the downloaded (or cached) file. public static async Task DownloadAndCacheFileAsync(string url) { - using (var client = new HttpClient() + var tedDirectory = Path.Combine(Path.GetTempPath(), "TED"); + var recentPath = Path.Combine(tedDirectory, "recent.png"); + + try { - Timeout = TimeSpan.FromSeconds(10) - }) - { - var response = await client.GetAsync(url); - response.EnsureSuccessStatusCode(); - - var etag = response.Headers.ETag?.Tag.Replace("\"", string.Empty) ?? "untagged"; - if (!Directory.Exists(Path.Combine(Path.GetTempPath(), "TED"))) + using (var client = new HttpClient() { - Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), "TED")); - } - var path = Path.Combine(Path.GetTempPath(), "TED", $"{etag}.png"); - - if (File.Exists(path)) + Timeout = TimeSpan.FromSeconds(10) + }) { - return path; - } + var response = await client.GetAsync(url); - var filesToDelete = Directory.GetFiles(Path.Combine(Path.GetTempPath(), "TED"), "*.png") - .Where(filePath => Path.GetFileNameWithoutExtension(filePath) != etag); - foreach (var fileToDelete in filesToDelete) - { - File.Delete(fileToDelete); - } - - using (var stream = await response.Content.ReadAsStreamAsync()) - { - using (var fileStream = new FileStream(path, FileMode.CreateNew)) + try { - await stream.CopyToAsync(fileStream); - return path; + response.EnsureSuccessStatusCode(); + } + catch (HttpRequestException e) + { + // If we catch here, it's a URL error or server-side issue. + if (File.Exists(recentPath)) + { + return recentPath; + } + + return string.Empty; + } + + 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) + { + File.Delete(fileToDelete); + } + + using (var stream = await response.Content.ReadAsStreamAsync()) + { + using (var fileStream = new FileStream(downloadPath, FileMode.CreateNew)) + { + await stream.CopyToAsync(fileStream); + 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; } }