Unity WebRequest实现下载信息回传并更新UI
实现下载进度实时更新UI的解决方案
问题背景
我有三个脚本:MenuManager、NetworkManager和自定义HttpClient。点击按钮会调用MenuManager的方法,触发NetworkManager从Asp.net后端下载数据。需要实现HttpClient将文件大小、下载速度、已下载总字节数实时回传给NetworkManager,再由NetworkManager传递到MenuManager更新UI。之前的修改尝试未成功,以下是正确的修改方案:
1. 修改HttpClient脚本
为PostWithProgress方法添加进度回调参数,在下载循环中实时触发回调,传递下载进度、速度、文件大小和已下载字节数:
public static async Task<T> PostWithProgress<T>(string endpoint, object payload, Action<float, float, long, ulong> progressCallback, string header = null) { var postRequest = CreateRequest(endpoint, RequestType.POST, payload); if (!string.IsNullOrEmpty(header)) AttachHeader(postRequest, "Bearer ", header); postRequest.SendWebRequest(); long fileSize = long.Parse(postRequest.GetResponseHeader("Content-Length")); ulong previousBytes = 0; float lastTime = Time.time; while (!postRequest.isDone) { float currentTime = Time.time; float timeDelta = currentTime - lastTime; if (timeDelta <= 0) { await Task.Delay(10); continue; } // 计算进度、已下载字节 float progress = postRequest.downloadProgress; ulong bytesDownloaded = postRequest.downloadedBytes; // 计算下载速度(字节/秒) float downloadSpeed = (bytesDownloaded - previousBytes) / timeDelta; // 实时触发回调,传递数据 progressCallback?.Invoke(progress * 100, downloadSpeed, fileSize, bytesDownloaded); previousBytes = bytesDownloaded; lastTime = currentTime; await Task.Delay(10); } if (postRequest.isNetworkError || string.IsNullOrEmpty(postRequest.downloadHandler.text)) { string errorMessage = ""; if (!postRequest.isHttpError) { errorMessage = postRequest.isNetworkError ? "Network Error" : string.IsNullOrEmpty(postRequest.downloadHandler.text) ? "Empty Response" : postRequest.error ?? "Test null"; } else { errorMessage = "Internal Server Error"; } GameObject.FindObjectOfType<ErrorLogger>(true).LogError(errorMessage, "Error: " + errorMessage); return default; } try { return JsonConvert.DeserializeObject<T>(postRequest.downloadHandler.text); } catch { GameObject.FindObjectOfType<ErrorLogger>(true).LogError(postRequest.downloadHandler.text, postRequest.downloadHandler.text); return default; } }
修改说明:
- 添加
Action<float, float, long, ulong> progressCallback参数,用于传递实时进度 - 优化下载速度计算:通过记录上一次的已下载字节和时间,计算时间差内的字节增量,得到更准确的实时速度
- 在循环中每次计算后调用
progressCallback,传递进度(转为百分比)、速度、文件大小、已下载字节数
2. 修改NetworkManager脚本
更新DownloadPlayerInfo方法,接收MenuManager传入的回调参数,并传递给HttpClient,同时在回调触发时直接转发给MenuManager:
public async Task DownloadPlayerInfo(Action<float, float, long, ulong> progressCallback) { UserInfoRequest req = new UserInfoRequest() { UID = UID }; var response = await HttpClient.PostWithProgress<UserInfoResponse>(userInfoEndpoint, req, progressCallback, PlayerToken); HandleUserInfoResponse(response); }
修改说明:
- 方法参数添加
Action<float, float, long, ulong> progressCallback - 调用
PostWithProgress时传入该回调,HttpClient的实时进度会直接通过这个回调传递给MenuManager - 下载完成后处理响应数据
3. 修改MenuManager脚本
在DownloadPlayerInfo方法中传入回调,实时接收进度数据并更新UI:
private async Task DownloadPlayerInfo() { LoginPanal?.SetActive(false); RegisterPanal?.SetActive(false); // 传入进度回调,实时更新UI await networkManager.DownloadPlayerInfo((progress, speed, fileSize, bytesDownloaded) => { UpdateDownloadProgress(progress, speed, fileSize, bytesDownloaded); }); } private void UpdateDownloadProgress(float progress, float downloadSpeed, long fileSize, ulong bytesDownloaded) { float speed = 0f; string abbreviation = "B/s"; if (downloadSpeed >= 1024 * 1024) { speed = downloadSpeed / (1024 * 1024); abbreviation = "MB/s"; } else if (downloadSpeed >= 1024) { speed = downloadSpeed / 1024; abbreviation = "KB/s"; } else { speed = downloadSpeed; abbreviation = "B/s"; } DownloadInfoText.text = $"{speed:F1} {abbreviation} Progress: {progress:F2}% " + $"({bytesDownloaded / (1024f * 1024f):F1}MB / {fileSize / (1024f * 1024f):F1}MB)"; }
修改说明:
- 调用
networkManager.DownloadPlayerInfo时传入匿名回调函数,接收进度数据 - 修复变量名拼写错误(abriviation → abbreviation)
- 计算MB时使用
1024f避免整数除法导致的精度丢失
内容的提问来源于stack exchange,提问作者Rudolf
相关产品推荐
相关产品推荐

