用HttpClient 异步 GET 超大文件方法

本文介绍了如何使用C#进行超大文件的异步GET下载,并提供了Python结合Flask实现的下载服务器代码,满足特定需求的扩展可能性。

c#的 下载代码。 还可以扩展更多,看自己需求了。

        /// <summary>
        /// Get方式获取文件,支持超大文件
        /// </summary>
        /// <param name="url">GET路径</param>
        /// <param name="data">get时带入body的数据</param>
        /// <param name="buffsize">缓存尺寸,默认51200</param>
        /// <param name="downloading">下载中事件</param>
        /// <param name="beforGet">Get前</param>
        /// <param name="beforReadStream">读取流前</param>
        /// <param name="setFilePath">设置文件名</param>
        /// <param name="afterGet">Get后,销毁response前</param>
        /// <param name="onException">异常处理,返回true抛出异常,返回false不抛异常</param>
        public async void GetToFile(string url, string data = "", int buffsize = 51200, 
            Action<DateTime, int, int> downloading = null, 
            Action<HttpRequestMessage> beforGet = null, 
            Action<HttpResponseMessage> beforReadStream = null, 
            Func<HttpResponseMessage, string> setFilePath = null, 
            Action<HttpResponseMessage> afterGet = null, 
            Func<Exception, bool> onException = null)
        {
            int intervals = 200; // downloading执行间隔
            int readsize = 1; // 每次读取到的尺寸
            int totalSize = 0; // 总读取尺寸
            int totalReadTimes = 0; // 总读取次数
            DateTime downloadStart = DateTime.Now;

            #region --默认的downloading委托--
            int currReport = 0, reportSize = 5;//这里reportSize实际上以每200次标准读取循环为基数,5就等于 5*200(见intervals)
            Action<DateTime, int, int> defaultDownloading = (stm, trt, ts) =>
                {
                    currReport++;
                    if (currReport == reportSize)
                    {
                        var tms = (DateTime.Now - stm).TotalSeconds;
                        var averageVelocity = (ts / Math.Ceiling(tms)) / 1000000;
                        WriteLine($"TOTAL_SECONDS:{tms} TOTAL_SIZE:{ts} TOTAL_TIMES:{trt} AVERAGE_VELOCITY:{averageVelocity:f2}Mb/S");
                        currReport = 0;
                    }
                };
            if (downloading == null)
            {
                downloading = defaultDownloading;
            }
            else // 如果不是空就在委托尾部加上此委托(多播委托)
            {
                downloading += defaultDownloading;
            }
            #endregion

            #region --默认的设置文件路径方法--
            if (setFilePath == null)
            {
                setFilePath = (resp) =>
                {
                    // 服务端带文件名 就用文件名,不带就用自定义
                    if (resp.Content.Headers.ContentDisposition.FileName.NullEmpty())
                    {
                        return $"datafile.{DateTime.Now.DateTimeID()}.tmp";
                    }
                    else
                    {
                        return resp.Content.Headers.ContentDisposition.FileName;
                    }
                };
            }
            #endregion

            #region --其他默认--
            if(beforReadStream == null)
            {
                beforReadStream = (resp) => {
                    WriteLine($"ContentType:{resp.Content.Headers.ContentType},ContentLength:{resp.Content.Headers.ContentLength},FileName:{resp.Content.Headers.ContentDisposition.FileName}");
                };
            }
            if (beforGet == null)
            {
                beforGet = (q) => { };
            }
            if(afterGet == null)
            {
                afterGet = (s) =>
                {
                    var total_seconds = (DateTime.Now - downloadStart).TotalSeconds;
                    var averageVelocity = (totalSize / Math.Ceiling(total_seconds)) / 1000000;
                    WriteLine($"TOTAL_SECONDS:{total_seconds} TOTAL_SIZE:{totalSize} TOTAL_TIMES:{totalReadTimes} AVERAGE_VELOCITY:{averageVelocity:f2}Mb/S DOWNLOAD END");
                };
            }
            if(onException == null)
            {
                onException = (ex) => true;
            }
            #endregion
            using (HttpClient client = new HttpClient())
            {
                try
                {
                    var uri = MakeFullUri(url);
                    HttpRequestMessage req = new HttpRequestMessage(new HttpMethod("GET"), uri);
                    req.Content = new StringContent(data);
                    beforGet(req);
                    using (var res = client.SendAsync(req, HttpCompletionOption.ResponseHeadersRead).Result)
                    {
                        beforReadStream(res);
                        string filePath = setFilePath(res);
                        downloadStart = DateTime.Now;
                        using (Stream contentStream = await res.Content.ReadAsStreamAsync())
                        using (FileStream fs = new FileStream(filePath, FileMode.OpenOrCreate))
                        {
                            byte[] buff = new byte[buffsize];
                            while (readsize > 0)
                            {
                                readsize = contentStream.Read(buff, 0, buffsize);
                                totalSize += readsize;
                                //contentStream.Flush();
                                fs.Write(buff, 0, readsize);
                                //fs.Flush();
                                totalReadTimes++;
                                // 符合间隔数才执行downloading方法
                                if ((totalReadTimes % intervals) == 0)
                                {
                                    fs.Flush();
                                    downloading(downloadStart, totalReadTimes, totalSize);
                                }
                            }
                        }
                        afterGet(res);
                    }
                    //return client.GetAsync(uri).Result.Content.ReadAsStringAsync();
                }
                catch (Exception ex)
                {
                    if (onException(ex))
                    {
                        throw ex;
                    }
                }
            }
        }

 

python+flask的 下载服务器 代码


from flask import Flask, abort, request, jsonify, Response, send_from_directory, make_response
from flask_cors import CORS
import time

@app.route('/download/<filename>', methods=['GET'])
def download(filename):
    # 这文件名参数 只是个装饰,没用
    data = request.get_data()
    print(data)
    directory = 'G:\\soft\\Power'
    filename = 'XXXXXXXXXXX.1428562995.exe'
    response = make_response(send_from_directory(directory, filename, mimetype='application/octet-stream', as_attachment=True))
    response.headers["Content-Disposition"] = f"attachment; filename={filename}"# {file_name.encode().decode('utf8')}
    return response

 

转载于:https://my.oschina.net/raddleoj/blog/3075595

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值