You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 13:19:56