一、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();

更多:

EF Core Sequence contains no elements

.Net Core3 新特性整理

.Net Core 实现 自定义Http的Range输出实现断点续传或者分段下载的更多相关文章

  1. ASP.NET Core MVC – 自定义 Tag Helpers

    ASP.NET Core Tag Helpers系列目录,共四篇: ASP.NET Core MVC Tag Helpers 介绍 ASP.NET Core MVC – Caching Tag Hel ...

  2. 如何在ASP.NET Core中自定义Azure Storage File Provider

    文章标题:如何在ASP.NET Core中自定义Azure Storage File Provider 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p ...

  3. ASP.NET Core Identity自定义数据库结构和完全使用Dapper而非EntityFramework Core

    前言 原本本节内容是不存在的,出于有几个人问到了我:我想使用ASP.NET Core Identity,但是我又不想使用默认生成的数据库表,想自定义一套,我想要使用ASP.NE Core Identi ...

  4. .Net Core IIS下无Log4Net日志输出,命令行下却有(dotnet运行)

    .Net Core IIS下无Log4Net日志输出,命令行下却有(dotnet运行) 遇到个诡异的问题,项目发布并寄宿到 IIS上后,Log4Net没有日志输出 1.原因分析 这不应该啊,所有的配置 ...

  5. ASP.NET Core AutoWrapper 自定义响应输出

    前言 AutoWrapper是一个简单可自定义全局异常处理程序和ASP.NET Core API响应的包装.他使用ASP.NET Core middleware拦截传入的HTTP请求,并将最后的结果使 ...

  6. asp.net core如何自定义端口/修改默认端口

    .net core运行的默认端口是5000,但是很多时候我们需要自定义端口.有两种方式 写在Program的Main方法里面 添加 .UseUrls() var host = new WebHostB ...

  7. ASP.NET Core中自定义路由约束

    路由约束 ASP.NET Core中,通过定义路由模板,可以在Url上传递变量,同时可以针对变量提供默认值.可选和约束. 约束的使用方法是在属性路由上添加指定的约束名,用法如下: // 单个使用 [R ...

  8. asp.net core razor自定义taghelper

    又一个新的名词(taghelper),这个名词在netcore razor中也替代了(Htmlhelper),通过taghelper是可以操作html标签.条件输出.更是自由添加内外元素.当然也内置了 ...

  9. ASP.NET Core 中间件自定义全局异常处理

    目录 背景 ASP.NET Core过滤器(Filter) ASP.NET Core 中间件(Middleware) 自定义全局异常处理 .Net Core中使用ExceptionFilter .Ne ...

随机推荐

  1. Java之Apache Commons-IO使用精讲

    Commons IO是针对开发IO流功能的工具类库.主要包括六个区域: 工具类--使用静态方法执行共同任务输入--用于InputStream和Reader实现输出--用于OutputStream和Wr ...

  2. php实现验证码(数字、字母、汉字)

    验证码在表单实现越来越多了,但是用js的写的验证码,总觉得不方便,所以学习了下php实现的验证码.好吧,其实是没有事情干,但是又不想浪费时间,所以学习了下php实现验证码.正所谓,技多不压身.而且,也 ...

  3. Photoshop 各混合模式 RGB 是如何计算的

    原文链接:https://www.jb51.net/photoshop/249182.html 1.正常模式(Normal) 默认模式,显示混合色图层的像素,没有进行任何的图层混合.这意味着基色图层( ...

  4. 使用 baget 搭建私有 nuget 私有服务

    现在几乎所有语言都提供包管理工具,比如 JavaScript 的 npm ,Java 的 Maven ,Dart 的 pub ..Net 程序当然是 NuGet .NuGet 也出现很多年了,奇怪的是 ...

  5. AFL++ Fuzz一个libexif例子

    CVE-2009-3895 首先在NVD找到漏洞描述如下: 大致意思是说:libexif 0.6.18 中的 libexif/exif-entry.c 中的 exif_entry_fix 函数中基于堆 ...

  6. windows/linux 页面编码区别导致 python 乱码

    http://blog.csdn.net/haiross/article/details/36189103 可以先看下这篇文章..写的比较用心和详细并且高深..我只是记流水账的. 直到今天我才注意到 ...

  7. web安全性测试——XSS跨站攻击

    1.跨站攻击含义 XSS:(Cross-site scripting)全称"跨站脚本",是注入攻击的一种.其特点是不对服务器端造成任何伤害,而是通过一些正常的站内交互途径,例如发布 ...

  8. Intel® QAT加速卡之Ring & Ring Bank

    1. QAT的应用模式 Intel 通讯系列芯片对于每种受支持的加速服务(加密,数据压缩),都支持以下应用模式: 内核模式,其中应用程序和加速服务都在内核中运行空间. 用户空间直接访问在用户空间中运行 ...

  9. ysoserial CommonsColletions2分析

    ysoserial CommonsColletions2分析 前言 此文章是ysoserial中 commons-collections2 的分析文章,所需的知识包括java反射,javassist. ...

  10. Java 扫描识别条形码图片

    1.条形码扫描识别的实现方法及步骤 本文以Java代码示例介绍如何来扫描和识别条形码图片.这里使用免费条码工具 Free Spire.Barcode for Java,调用BarcodeScanner ...