月色真美

月色真美

.Net下使用HttpClient实现进度条效果

99
2023-10-05

从.Net6开始,WebRequest就已被标识已过时,微软推荐使用HttpClient进行替代。在查阅了相关文档后,整理了一个在HttpClient上实现进度条效果的下载封装。

PS:目前测试结果,当网络环境处于特殊代理环境下时(如clash代理),HttpCompletionOption.ResponseHeadersRead不被支持,推测可能是代理转发存在某些缺陷造成。

/// <summary>
/// 下载文件
/// </summary>
/// <param name="fullFileName">保存路径</param>
/// <param name="url">资源地址</param>
/// <param name="callback">回调</param>
/// <param name="progress">进度</param>
/// <returns></returns>
public static async Task<bool> DownloadAsync(string fullFileName, string url, Action<bool, string> callback, Action<int> progress)
{
    if (File.Exists(fullFileName))
        File.Delete(fullFileName);

    try
    {
        using (var client = new System.Net.Http.HttpClient())
        {
            // HttpCompletionOption.ResponseHeadersRead
            // 即得到header后就响应结果,不必等待内容,也是实现进度效果的核心代码
            using (var response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead))
            {
                response.EnsureSuccessStatusCode();
                var length = response.Content.Headers.ContentLength;
                long readLength = 0;
                using (var responseStream = await response.Content.ReadAsStreamAsync())
                {
                    using (var stream = new FileStream(fullFileName, FileMode.Create))
                    {
                        byte[] byteArry = new byte[1024];
                        int size = responseStream.Read(byteArry, 0, (int)byteArry.Length);
                        readLength += size;
                        while (size > 0)
                        {
                            stream.Write(byteArry, 0, size);
                            if (progress != null)
                                progress((int)(readLength * 100 / length));

                            size = responseStream.Read(byteArry, 0, (int)byteArry.Length);
                            readLength += size;
                        }
                    }
                }
            }
        }

        if (callback != null)
            callback(true, fullFileName);

        return true;
    }
    catch (Exception ex)
    {
        if (callback != null)
            callback(false, ex.Message);

        return false;
    }
}