.NET Framework 4.8下HttpClient下载文件延迟显示问题求助
问题描述
我为此问题研究了一周,尝试了大量搜索与多种测试方法。使用HttpClient下载文件时无报错,但文件需等程序退出后才会出现在指定文件夹中。我有一个无法修改为异步的同步方法,用于调用异步方法下载Tesseract OCR的语言文件(测试使用语言为"eng"):
if (!Directory.Exists(folderName)) Directory.CreateDirectory(folderName); Task.Run(async () => await HelperMethods.LoadLanguage(folderName, language)); Task.Run(async () => await HelperMethods.LoadLanguage(folderName, "osd"));
被调用的异步方法:
public static async Task LoadLanguage(string folderName, string language) { string dest = Path.GetFullPath(Path.Combine(folderName, $"{language}.traineddata")); if (!File.Exists(dest)) { // Now we know that we need network - start it up if it isn't already. if (httpClient == null) httpClient = new HttpClient(); Uri uri = new Uri($"https://github.com/tesseract-ocr/tessdata/raw/main/{language}.traineddata"); HttpResponseMessage response = await httpClient.GetAsync(uri); using (FileStream fs = new FileStream(dest, FileMode.Create, FileAccess.Write)) { await response.Content.CopyToAsync(fs); await fs.FlushAsync(); fs.Close(); } } }
我已添加Flush和Close操作进行测试,但无任何改善。这些文件本应下载完成后供后续OCR操作使用,但仅在程序退出后才会显示在文件夹中。第二次运行程序时可正常工作,因为无需重新下载文件。我还尝试了以下方法:
var request = new HttpRequestMessage(HttpMethod.Get, uri); var sendTask = httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); var response = sendTask.Result.EnsureSuccessStatusCode(); var httpStream = await response.Content.ReadAsStreamAsync(); using (var fileStream = File.Create(dest)) { using (var reader = new StreamReader(httpStream)) { httpStream.CopyTo(fileStream); fileStream.Flush(); } }
结果仍无变化,我还尝试了多种流操作方法。当前环境为.NET Framework 4.8,无法升级到.NET6。请问如何让文件下载后立即保存,以便后续操作可以使用?
解决方案
- 核心问题是你启动的异步下载任务未被等待:
Task.Run创建的后台任务在主线程继续执行后续逻辑时仍在后台运行,后续OCR操作会在文件未完全写入时尝试访问;若主线程先于后台任务结束,后台任务可能被中断,直到进程退出前才完成写入。 - 由于调用方法是同步的,无法改为异步,需在同步方法中等待所有下载任务完成,修改调用代码如下:
if (!Directory.Exists(folderName)) Directory.CreateDirectory(folderName); // 收集所有下载任务 var downloadTasks = new List<Task> { HelperMethods.LoadLanguage(folderName, language), HelperMethods.LoadLanguage(folderName, "osd") }; // 同步阻塞,等待所有下载任务完成 Task.WhenAll(downloadTasks).Wait();
- 同时优化
LoadLanguage方法的细节:- 给
HttpClient添加线程安全的单例初始化,避免频繁创建销毁实例 - 移除手动调用的
fs.Close(),using块会自动释放流资源 - 添加
response.EnsureSuccessStatusCode()确保请求成功,避免下载失败时生成空文件 - 使用
FlushAsync(true)强制将数据写入磁盘(而非操作系统缓存),确保文件立即可见
- 给
修改后的LoadLanguage方法:
private static HttpClient _httpClient; public static async Task LoadLanguage(string folderName, string language) { string dest = Path.GetFullPath(Path.Combine(folderName, $"{language}.traineddata")); if (File.Exists(dest)) { return; } // 线程安全初始化HttpClient if (_httpClient == null) { Interlocked.CompareExchange(ref _httpClient, new HttpClient(), null); } Uri uri = new Uri($"https://github.com/tesseract-ocr/tessdata/raw/main/{language}.traineddata"); HttpResponseMessage response = await _httpClient.GetAsync(uri); // 确保请求成功,否则抛出异常 response.EnsureSuccessStatusCode(); using (FileStream fs = new FileStream(dest, FileMode.Create, FileAccess.Write)) { await response.Content.CopyToAsync(fs); // 强制将缓冲数据写入磁盘 await fs.FlushAsync(true); } }
内容的提问来源于stack exchange,提问作者RobCole
相关产品推荐
相关产品推荐

