C# WebClient的DownloadFileAsync执行正常但无法下载文件问题求助
问题原因
- WebClient局部变量被GC提前回收:你代码中
WebClient是Update方法内的局部变量,调用DownloadFileAsync后方法会立即返回,此时局部变量wc没有任何根引用,GC运行时会直接回收该对象,导致还在执行的异步下载任务被强制终止,文件自然无法完整写入甚至不会被创建。 - 异步异常未被捕获:
DownloadFileCompleted事件回调中的异常不会被Update方法内的try/catch捕获,就算下载过程中出现报错,上层逻辑也感知不到,所以你会得到「代码运行无报错」的假象。 - 若你的程序是控制台程序、或者调用
Update方法后进程很快退出,异步下载还没执行完进程就结束,也会导致没有生成目标文件。
解决方法
- 将WebClient改为类级成员变量,避免被GC提前回收,示例修改代码如下:
// 把WebClient声明在类层级,不要放在Update方法内部 private WebClient _wc; /// <summary> /// 下载更新 /// </summary> public string Update() { string result = ""; try { if (!isUpdate) return result; // 初始化类级别的WebClient _wc = new WebClient(); string filename = "Update_NewVersion.zip"; // 建议用Path.Combine拼接路径,避免手动写斜杠出错 filename = Path.Combine(Path.GetDirectoryName(loadFile), filename); FinalZipName = filename; _wc.DownloadFileCompleted += new AsyncCompletedEventHandler(wc_DownloadFileCompleted); _wc.DownloadProgressChanged += new DownloadProgressChangedEventHandler(wc_DownloadProgressChanged); _wc.DownloadFileAsync(new Uri(download), filename); return result = $"download:{download},filename:{filename}"; } catch (Exception ex) { throw new Exception("更新出现错误,网络连接失败!" + ex.Message); } } void wc_DownloadFileCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e) { (sender as WebClient).Dispose(); // 不要直接抛异常,改成记录错误或者通过回调/事件通知上层 if (e.Error != null) { // 此处可写入日志、或者定义错误事件对外通知 Console.WriteLine($"下载出错:{e.Error.Message}"); _wc = null; return; } _wc = null; isFinish(); }
- 如果需要等待下载完成再执行后续逻辑,可将基于事件的异步模式转为Task等待,避免提前释放资源:
public async Task<string> UpdateAsync() { string result = ""; try { if (!isUpdate) return result; using var wc = new WebClient(); string filename = "Update_NewVersion.zip"; filename = Path.Combine(Path.GetDirectoryName(loadFile), filename); FinalZipName = filename; wc.DownloadProgressChanged += (s, e) => { CommonMethod.autostep = e.ProgressPercentage; }; // 用TaskCompletionSource包装异步事件 var tcs = new TaskCompletionSource<bool>(); wc.DownloadFileCompleted += (s, e) => { if (e.Error != null) tcs.SetException(e.Error); else if (e.Cancelled) tcs.SetCanceled(); else tcs.SetResult(true); }; wc.DownloadFileAsync(new Uri(download), filename); // 等待下载完成才会返回,不会提前释放WebClient await tcs.Task; return result = $"download:{download},filename:{filename}"; } catch (Exception ex) { throw new Exception("更新出现错误,网络连接失败!" + ex.Message); } }
- 推荐替代方案:WebClient已经被微软官方标记为过时类型,优先使用HttpClient实现异步下载,兼容性和稳定性更好:
// HttpClient建议全局声明复用,不要每次请求都新建 private static readonly HttpClient _httpClient = new HttpClient(); public async Task DownloadUpdateAsync(string downloadUrl, string savePath) { using var response = await _httpClient.GetAsync(downloadUrl, HttpCompletionOption.ResponseHeadersRead); // 校验请求是否成功 response.EnsureSuccessStatusCode(); var totalLength = response.Content.Headers.ContentLength ?? 0; using var contentStream = await response.Content.ReadAsStreamAsync(); using var fileStream = new FileStream(savePath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true); var buffer = new byte[8192]; long totalRead = 0; int read; while ((read = await contentStream.ReadAsync(buffer)) > 0) { await fileStream.WriteAsync(buffer.AsMemory(0, read)); totalRead += read; if (totalLength > 0) { // 计算下载进度 CommonMethod.autostep = (int)((double)totalRead / totalLength * 100); } } isFinish(); }
内容的提问来源于stack exchange,提问作者小确幸
相关产品推荐
相关产品推荐

