首页 > 其他分享 >.Net Core 实现 自定义Http的Range输出实现断点续传或者分段下载

.Net Core 实现 自定义Http的Range输出实现断点续传或者分段下载

时间:2023-12-25 09:00:31浏览次数:54  
标签:Core fs Http 自定义 int Range context end public

一、Http的Range请求头,结合相应头Accept-Ranges、Content-Range 可以实现如下功能:

1.断点续传。用于下载文件被中断后,继续下载。

2.大文件指定区块下载,如视频、音频拖动播放,直接定位到指定位置下载内容。可以避免每次都读取、传输整个文件,从而提升服务端性能。

3.大文件分包批量下载,再合并完整文件。可以提高下载速度。

 

二、Http的Range 相关说明:

1.规则要点

 请求头Range表示请求的数据起始位置。响应头Accept-Ranges:bytes 表示支持续传。响应头Content-Range表示返回的其实位置、总长度

请求头Range的数字,首尾都包含,长度是: end-begin+1

请求头Range的指定的长度,只是意向下载量,服务端不一定返回请求的长度。比如:bytes=0-, 表示希望下载整个文件,但服务端可以返回有限长度的数据块(有利于性能)。但数据其实位置start需按照请求。

2.在Http 响应请求是 200,表示响应结束,响应成功

Http 响应状态:206,表示响应中,响应部分数据,不会单开Socket链接。

 

三、在Asp.Net Core中实现自定义 Range 文件响应

1.封装处理的类:

复制代码
    public class DownloadRange
    {

        public HttpContext context = null;
        public HttpRequest request = null;
        public HttpResponse response = null;
        public DownloadRange(HttpContext ctx)
        {
            this.context = ctx;
            this.request = ctx.Request;
            this.response = ctx.Response;
        }
        private int HttpRangeSize = 1024 * 1024; //最大块长度 1M
        public void WriteFile(string file)
        {
            using (var fs = File.OpenRead(file))
            {
                WriteStream(fs);
            }
        }
        private  void WriteStream(Stream fs)
        {
            string range = request.Headers["Range"];
            range = range ?? "";
            range = range.Trim().ToLower();
            if (fs.CanSeek)
            {
                if (range.StartsWith("bytes=") && range.Contains("-"))
                {
                    //分段输出文件
                    int start = -1, end = -1;
                    var rgs = range.Substring(6).Split('-');
                    int.TryParse(rgs[0], out start);
                    int.TryParse(rgs[1], out end);
                    if (rgs[0] == "")
                    {
                        start = (int)fs.Length - end;
                        end = (int)fs.Length - 1;
                    }
                    if (rgs[1] == "")
                    {
                        end = (int)fs.Length - 1;
                    }
                    WriteRangeStream(fs, start, end);
                }
                else
                {
                    //输出整个文件
                    int l;
                    byte[] buffer = new byte[40960];
                    while ((l = fs.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        response.Body.Write(buffer, 0, l);
                    }
                }
            }
        }
        private  void WriteRangeStream(Stream fs, int start, int end)
        {
            using (fs)
            {
                int rangLen = end - start + 1;
                if (rangLen > 0)
                {
                    if (rangLen > HttpRangeSize)
                    {
                        rangLen = HttpRangeSize;
                        end = start + HttpRangeSize - 1;
                    }
                }
                else
                {
                    throw new Exception("Range error");
                }

                long size = fs.Length;
                //如果是整个文件返回200,否则返回206
                if (start == 0 && end + 1 >= size)
                {
                    response.StatusCode = 200;
                }
                else
                {
                    response.StatusCode = 206;
                }
                // response.Headers.Add("Accept-Ranges", "bytes");
                response.Headers.Add("Content-Range", $"bytes {start}-{end}/{size}");
                response.Headers.Add("Content-Length", rangLen.ToString());

                int readLen, total = 0;
                byte[] buffer = new byte[40960];
                //流定位到指定位置
                try
                {
                    fs.Seek(start, SeekOrigin.Begin);
                    while (total < rangLen && (readLen = fs.Read(buffer, 0, buffer.Length)) > 0)
                    {
                        total += readLen;
                        if (total > rangLen)
                        {
                            readLen -= (total - rangLen);
                            total = rangLen;
                        }
                        response.Body.Write(buffer, 0, readLen);
                    }
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }
    }
复制代码

 

2.自定义中间件,处理文件输出

复制代码
    public class OuterImgMiddleware
    {
        public static string RootPath { get; set; } //配置文件读取绝对位置
        private readonly RequestDelegate _next;
        public OuterImgMiddleware(RequestDelegate next, Microsoft.AspNetCore.Hosting.IHostingEnvironment env)
        {
            _next = next;
        }
        public async Task Invoke(HttpContext context)
        {
            var path = context.Request.Path.ToString();
            var headersDictionary = context.Request.Headers;

            if (context.Request.Method == "GET" && !string.IsNullOrEmpty(path))
            {
                if (
                    path.Contains("/upload/logo")
                    || path.Contains("/upload/image")
                    || path.Contains("/upload/ueimage")
                    )
                {
                    var unauthorizedImagePath = RootPath + path;
                    FileInfo file = new FileInfo(unauthorizedImagePath);
                    if (file.Exists)
                    {
                        int length = path.LastIndexOf(".") - path.LastIndexOf("/") - 1;
                        context.Response.Headers["Etag"] = path.Substring(path.LastIndexOf("/") + 1, length);
                        context.Response.Headers["Last-Modified"] = new DateTime(2018).ToString("r");
                        context.Response.Headers["Accept-Ranges"] = "bytes";
                        //context.Response.Headers["Content-Length"] = file.Length.ToString();
                        if (path.EndsWith(".mp4"))
                        {
                            context.Response.ContentType = "video/mp4";
                            //分段读取内容
                            DownloadRange download = new DownloadRange(context);
                            download.WriteFile(unauthorizedImagePath);
                        }
                        else
                        {
                            context.Response.ContentType = "image/jpeg";
                            context.Response.Headers["Cache-Control"] = "public"; //指定客户端,服务器都处理缓存
                            await context.Response.SendFileAsync(unauthorizedImagePath);
                        }
                    }
                    return;
                }
            }

            await _next(context);
        }
    }
    public static class MvcExtensions
    {
        public static IApplicationBuilder UseOutImg(this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<OuterImgMiddleware>();
        }
    }
复制代码

 

3. 在服务配置中 ConfigureServices,开启同步读取

          //启用允许同步读取
            services.Configure<KestrelServerOptions>(x => x.AllowSynchronousIO = true)
              .Configure<IISServerOptions>(x => x.AllowSynchronousIO = true);

4.在配置中 Configure,启用中间件

 

app.UseOutImg();

标签:Core,fs,Http,自定义,int,Range,context,end,public
From: https://www.cnblogs.com/0624zfz/p/17925246.html

相关文章

  • Remove TraceParent header from HttpClient requests
    ASP.NETCorecreatesanActivitythatrepresentstherequestbydefault.ThisiswhattellsHttpClienttosendanoutgoingrequestidheader.Youcandisableitinacoupleofways:Bysettingthecurrentactivitytonullbeforemakingtherequest(Activi......
  • spring:Exception in thread "main" java.lang.NoClassDefFoundError: org/springframe
     设置了父类框架<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.1.10.RELEASE</version><relativePath/><!--l......
  • ASP.NET Core 6(.NET 6) 修改默认端口的方法(5000和5001)
    ​ ASP.NETCore6(.NET6)默认将HTTP端口绑定到5000,将HTTPS端口绑定到5001。可以通过以下三种方式修改默认端口:详细文档:ASP.NETCore6(.NET6)修改默认端口的方法(5000和5001)-CJavaPy方法一:修改launchSettings.json文件在项目的根目录下,找到launchSettings.jso......
  • Nginx 下将 http 改为 https
    将服务从HTTP变为HTTPS,需要进行SSL证书的配置。需要完成一下步骤:获取SSL证书安装SSL证书配置Nginx支持HTTPS重启NginxHTTP到HTTPS的重定向(可选)1.获取SSL证书(自签名证书)对于SSL/TLS证书,一般来说,它们是基于域名进行颁发的,而不是IP地址。这是因为S......
  • Java登陆第二十九天——HttpServletRequest和HttpServletResponse
    HttpServletRequestTomcat会自动将客户端请求报文封装为HttpServletRequest对象。HttpServletRequest中请求行常用方法方法描述StringgetMethod()获取请求方法StringgetProtocol()获取请求协议及版本号StringgetRequestURI()获取请求的具体资源StringB......
  • MappingJackson2HttpMessageConverter使用及jackson配置原理和避坑说明
    转载自:https://blog.csdn.net/Heron22/article/details/109512976MappingJackson2HttpMessageConverter消息转换器创建和生效原理HttpMessageConverters对象的创建使用WebMvcConfigurationSupport配置时转换器创建过程使用WebMvcAutoConfiguration配置时转换器创建过......
  • 基于Feigh发送Http请求,替代RestTemplate
    下载依赖<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId></dependency>需要在启动类上开启配置,Feigh默认自带负载均衡配置@SpringBootApplication@EnableFeignClientspublic......
  • Http网络协议包
    Http网络协议包一。网络协议包:1.在网络中传递信息都是以【二进制】形式存在的。 2.接收方【浏览器/服务器】在接收信息后,要做第一件事,就是将【二进制数据】进行编译【文字,图片,视频,命令】3.传递信息数据量往往比较巨大,导致接收方很难在一组连续二进制得到对应......
  • java 判断 https证书到期
    Java判断HTTPS证书到期概述在Java中,我们可以使用SSLContext和HttpsURLConnection来判断HTTPS证书是否过期。本文将介绍整个流程,并提供相应代码和注释。流程图下面是整个判断HTTPS证书到期的流程图:sequenceDiagramparticipant客户端participant服务器客户......
  • Jmeter:http请求及json断言
    一前言环境:window10jmeter5.3对jmeter的http请求和json断言这2个组件中的一些字段进行简单说明二http请求如上,可以选择切换语言,有时切换成中文或者英文,这样需要填写字段的意思更加一目了然三json断言断言请求返回的json数据数时,jmeter中默认有2种方式可选,如下这里......