Asp.Net Core 实现分片下载的最简单方式

技术群里的朋友遇到了这个问题,起初的原因是他对文件增加了一个属性配置

fileResult.EnableRangeProcessing = true;

这个属性我从未遇到过,然后,去F1查看这个属性的描述信息也依然少的可怜,只有简单的描述为(获取或设置为 启用范围处理 FileResult的值)。

范围处理,很容易理解,可能就是实现分片下载的关键,但是,是不是就很简单配置就可以了呢,我对此十分感兴趣,就开始查询它的实际信息。

EnableRangeProcessing 属性的含义

经查看Asp.net Core 源码可以看到,它实际上只是配置了一个头信息。


具体的方法内部,只是一个赋值


赋值的是什么呢,其实就是一个HTTP 头标记

所以,实际上,它只做了一件事情,那就是把请求的头变成以下的样子。

是的,它只是做了这一件事情,那就是把请求头 Accept-Ranges 的值设置为了 bytes 。

HTTP 协议头之 Accept-Ranges

经查寻,此头就是表示着 (Accept-Ranges HTTP 响应标头是服务器使用的一个标记,用于向客户端宣传其对文件下载的部分请求的支持。此字段的值表示可用于定义范围的单位。 )

当存在 Accept-Ranges 标头时,浏览器可能会尝试恢复中断的下载,而不是尝试重新启动下载。

所以,设置它就相当于设置了Asp.net Core 支持分片下载。

Asp.net Core 分片下载实际案例

主要分为服务端和客户端,服务端设置允许分片下载,客户端则需要按照分片进行多线程下载,我这里实际通过并行下载实现。
最后的验证,能运行它或者打开它即可(比如zip格式等)。

服务端代码

服务端异常的简单,新建一个Asp.net Core 项目即可,其他都默认。

我们要做的事情只是在 HomeController 里增加以下代码

private FileExtensionContentTypeProvider provider = new FileExtensionContentTypeProvider();
public IActionResult Down()
{var file = @"H:\百度网盘\ubuntu.zip";provider.TryGetContentType(file, out var contentType);var result = PhysicalFile(file, contentType);result.EnableRangeProcessing = true;result.FileDownloadName = Path.GetFileName(file);return result;
}

里面有几个点

  1. 设置文件的类型可以用 FileExtensionContentTypeProvider 这个类来的,不用自己写(特殊类型它不支持)
  2. 设置EnableRangeProcessing 为 true ,才能实现分片下载
  3. 设置FileDownloadName为具体的下载文件名,才可以在客户端知道你下载的文件名的名字,很重要。

客户端代码

客户端稍微复杂一些,得获取文件的大小和名字,然后,进行多线程下载,我这边会进行一个简单的模拟。

直接下载文件的逻辑
public static async Task DownUrl(string url)
{var result = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, url), HttpCompletionOption.ResponseHeadersRead);if (result.IsSuccessStatusCode){var filename = "temp.zip";using (var contentStream = await result.Content.ReadAsStreamAsync()){using (var file = File.OpenWrite(filename)){await contentStream.CopyToAsync(file);}}}
}

当然,如果不想分片下载,可以直接下载即可。

获取文件大小以及名字
public static async Task<(long? length, string filename)> GetFileLengthandName(string url)
{var result = await client.SendAsync(new HttpRequestMessage(HttpMethod.Get, url), HttpCompletionOption.ResponseHeadersRead);if (result.IsSuccessStatusCode){return (result.Content.Headers.ContentLength, result.Content.Headers.ContentDisposition.FileName);}return (null, null);
}

主要就是通过httpclient 自带的头信息,直接获取即可。挺简单的。

分片下载核心

首先是对获取到的文件大小进行一个范围的分割

public struct BytesRange
{public long Start { get; set; }public long End { get; set; }public long Length { get { return End - Start + 1; } }public override string ToString(){return $"{Start} {End} : {Length}";}public static List<BytesRange> GetRanges(long length, long BufferSize = 1 * 1024 * 1024){List<BytesRange> list = new List<BytesRange>();long count = length / BufferSize;long Lost = length - BufferSize * count;if (Lost > 0){list.Add(new BytesRange() { Start = count * BufferSize, End = count * BufferSize + Lost - 1 });}if (count > 0){for (long i = 0; i < count; i++){list.Add(new BytesRange() { Start = i * BufferSize, End = (i + 1) * BufferSize - 1 });}}else{list.Add(new BytesRange() { Start = 0, End = Lost - 1 });}list.OrderByDescending(t => t.Start);return list;}
}

这样就可以获取具体的分片信息,具体每片的大小。

public static async Task<byte[]> GetBytesAsync(string url, BytesRange range)
{var request = new HttpRequestMessage(HttpMethod.Get, url);request.Headers.Add("Range", $"bytes={range.Start}-{range.End}");using (HttpResponseMessage response = await client.SendAsync(request)){using (Stream stream = await response.Content.ReadAsStreamAsync()){if (range.Length != stream.Length){throw new Exception("数据不匹配!");}byte[] bytes = new byte[stream.Length];stream.Read(bytes, 0, bytes.Length);return bytes;}}
}

GetBytesAsync 就是按照指定的大小分为进行请求,并返回所需的文件大小。

实际代码
static HttpClient client = new HttpClient();
static object lockObj = new object();
static async Task Main(string[] args)
{var url = "http://localhost:5034/home/down";Stopwatch stopwatch = Stopwatch.StartNew();await DownUrl(url);stopwatch.Stop();Console.WriteLine($"单线程 直接下载耗时:{stopwatch.Elapsed.TotalSeconds}");stopwatch.Restart();(long? length, string filename) = await GetFileLengthandName(url);if (length.HasValue){var number = 10;//获取分片大小,默认1M 缓存区,太小又太慢 设置成5M。var list = BytesRange.GetRanges(length.Value, 5 * 1024 * 1024);Console.WriteLine($"分片数:{list.Count} 每片大小:5MB 并发数:{number}");var path = Path.Combine(AppContext.BaseDirectory, filename);using (var write = File.OpenWrite(path)){write.SetLength(length.Value);await write.FlushAsync();// 并行下载,每秒默认10并发Parallel.ForEach(list, new ParallelOptions() { MaxDegreeOfParallelism = number }, range =>{//Console.WriteLine($"{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {range}");var bytes = GetBytesAsync(url, range).Result;lock (lockObj){write.Seek(range.Start, SeekOrigin.Begin);write.Write(bytes);}});}Console.WriteLine("下载完毕,请验证!");}else{Console.WriteLine("没有获取到下载文件的信息!");}stopwatch.Stop();Console.WriteLine($"并发下载 耗时:{stopwatch.Elapsed.TotalSeconds}秒");Console.ReadLine();
}

验证结果

先运行服务端

再运行下载客户端

看到结果后,有点差异

从结果上来看,直接下载是最快的,应该是少了分片的开销,而且服务都是在本机上,各种IO的限制基本上只有文件IO,带宽IO影响最小。

总结

虽然直接下载是最快的,但是,如果网络中断的话,基本得重新下载,所以,它的风险反而是最高的,而分片下载虽然有了分片的开销的,但是可以从断点处继续下载,风险反而最低,各有优势。

代码地址

https://github.com/kesshei/WebDown.git

https://gitee.com/kesshei/WebDown.git

参考资料地址

《enableRangeProcessing 的代码地址》
https://github.com/dotnet/aspnetcore/blob/53db4d97d7c77d13e20e58a98f104e88d6af6040/src/Shared/ResultsHelpers/FileResultHelper.cs#L141
《Accept-Ranges 》
https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Accept-Ranges

一键三连呦!,感谢大佬的支持,您的支持就是我的动力!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/20770.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Golang | Leetcode Golang题解之第125题验证回文串

题目&#xff1a; 题解&#xff1a; func isPalindrome(s string) bool {s strings.ToLower(s)left, right : 0, len(s) - 1for left < right {for left < right && !isalnum(s[left]) {left}for left < right && !isalnum(s[right]) {right--}if l…

GIS Java 生成四至图

目录 前言 操作步骤&#xff1a; 1&#xff0c;求出多边形的四至点 2&#xff0c;下载地图 3&#xff0c;绘制多边形 前言 对于地图上的一个多边形地块&#xff0c;其四至图就是能够覆盖这个多边形的最小矩形&#xff0c;也就是求出这个多边形的最东点&#xff0c;最西点&…

关于GPT-4o的使用感受和评价

如何评价GPT-4o? 简介&#xff1a; 最近&#xff0c;GPT-4o横空出世。GPT-4o的发布无疑是人工智能领域的一次重大飞跃&#xff0c;它不仅仅是一个技术产品的迭代&#xff0c;更是人机交互理念的一次革新。作为OpenAI倾力打造的最新旗舰模型&#xff0c;GPT-4o在前代产品的基础…

Vue.js2+Cesium1.103.0 十六、多模型轨迹运动

Vue.js2Cesium1.103.0 十六、多模型轨迹运动 Demo <template><div id"cesium-container" style"width: 100%; height: 100%;"><ul class"ul"><li v-for"(item, index) of deviceInfo" :key"index" cl…

制作ChatPDF之前端Vue搭建(二)

前端界面 接上篇: 制作ChatPDF之Elasticsearch8.13.4搭建&#xff08;一&#xff09; 为了实现一个基于 Vue.js 的前端应用&#xff0c;用户可以上传 PDF 文件&#xff0c;输入查询&#xff0c;并在输出框中显示查询结果&#xff0c;你需要以下步骤&#xff1a; 初始化 Vue …

AIGC商业案例实操课,发觉其创造和商业的无限可能,Ai技术在行业应用新的商机

课程下载&#xff1a;https://download.csdn.net/download/m0_66047725/89307523 更多资源下载&#xff1a;关注我。 课程内容 1 AI为什么火 。写在课程前面的寄语 。AIGC标志性事件:太空歌剧院 。AI人工智能为什么这么火 &#xff0c;AI人工智能发展历程 。聊天AI会取…

【U-Boot 源码深度解析】001 - Ubuntu 24.04 虚拟机 及 基础环境搭建

【U-Boot 源码深度解析】001 - Ubuntu 24.04 虚拟机 及 基础环境搭建 一、VMware 、Ubuntu 安装包下载二、解决VMware NET 模式&#xff0c;虚拟机无法上网问题三、配置 Ubuntu 24.04 开发环境3.1 配置清华源 sources3.2 配置 Telnetd 服务3.3 配置 SSH 服务2.4 配置 samba 服务…

使用autodl服务器进行模型训练

1.注册并且选择一个服务器租用 2.点击jupyter lab进入服务器内部 3.把yolov5-master这个的压缩文件上传到jupyter的文件列表中 4.打开终端 (1)查看目录 ls (2)解压yolov5-master(1) unzip "yolov5-master (1).zip" 可以看到解压成功&#xff01; (3)进入yolov5-m…

[数据集][目标检测]焊接处缺陷检测数据集VOC+YOLO格式3400张8类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;3400 标注数量(xml文件个数)&#xff1a;3400 标注数量(txt文件个数)&#xff1a;3400 标注…

基于Chan-Vese算法的图像边缘提取matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 ............................................................ % 迭代更新水平集函数 err[]…

亚马逊对IP的要求是什么?

IP的全称为Internet Protocol&#xff0c;是TCP/IP体系中的网际层协议&#xff0c;IP只为主机提供一种无连接、不可靠的、尽力而为的数据包传输服务。IP规定网络上所有的设备都必须有一个独一无二的IP地址&#xff0c;就好比是邮件上都必须注明收件人地址&#xff0c;邮递员才能…

函数递归输出1~100的数字及递归的栈溢出问题

什么是递归&#xff1f; 递归就是函数⾃⼰调⽤⾃⼰递归中的递就是递推的意思&#xff0c;归就是回归的意思如果递归就像循环一样&#xff0c;打一个大的复杂问题转化一个小的问题&#xff0c;但是要与原问题相似&#xff0c;分解成规模较⼩的⼦问题来求解&#xff1b;直到⼦问…

【scau大数据技术与原理2】综合性实验Spark集群的安装和使用——安装启动spark shell篇

实验内容简介&#xff1a; Spark是一个分布式计算框架&#xff0c;常用于大数据处理。本次实验中&#xff0c;首先设计一个包含主节点和从节点的Spark集群架构&#xff0c;并在CentOS的Linux环境下进行搭建。通过下载并解压Spark安装包&#xff0c;配置环境变量和集群参数&…

【Python Cookbook】S1E09 对切片命名

目录 问题解决方案讨论 问题 代码的可阅读性非常重要&#xff0c;如何增强切片中的可阅读性&#xff1f;本文将提供一种方案。 解决方案 假设有一些代码用来从字符串的固定位置取出具体的数据&#xff1a; record "...100...513.25..." cost int(record[3:6]) …

【显示方案IC-速显微】

最近偶然间接触到“速显微”的显示方案&#xff0c;个人体验了一把感觉还是挺顺手的&#xff0c;虽然手里没有板子没有上手测试一番。 这是他们的官网链接&#xff1a; https://www.thorsianway.com/product/chip 从官网可以看到有两颗个系列的IC已经量产&#xff1a;GC9005和G…

Docker部署pulsar独立集群消息队列服务器

1、下载Pulsar docker 镜像 docker pull apachepulsar/pulsar:latest 2、生成Pulsar容器&#xff0c;把容器的6650和8080端口映射到宿主机的6650和8080端口&#xff0c;standalone参数表示pulsar为独立集群模式&#xff0c;把容器中的conf目录映射到宿主机的/var/lib/docker/…

【QEMU中文文档】1.1 支持的构建平台

本文由 AI 翻译&#xff08;ChatGPT-4&#xff09;完成&#xff0c;并由作者进行人工校对。如有任何问题或建议&#xff0c;欢迎联系我。联系方式&#xff1a;jelin-shoutlook.com。 原文&#xff1a;Supported build platforms — QEMU documentation QEMU 旨在支持在多个主机…

基础—SQL—DCL(数据控制语言)小结

一、总结 在SQL分类中的DCL语句部分&#xff0c;主要讲到了两个部分的知识。 1、用户管理 用户管理&#xff0c;主要是管理哪些用户可以访问当前 mysql 数据库。 包括&#xff1a;创建用户、修改用户密码以及删除用户 2、权限控制 权限管理&#xff0c;主要是控制我们当前用户…

vue前端Echars

<template><div :class"className" :style"{height:height,width:width}" /> </template><script> import * as echarts from echarts require(echarts/theme/macarons) // echarts theme 柱状图 import resize from ./mixins/re…

禁用USB端口的办法,哪一种禁用USB端口的方法好

禁用USB端口的办法&#xff0c;哪一种禁用USB端口的方法好 禁用USB端口是保护公司数据安全的一种常见做法&#xff0c;旨在防止未经授权的数据传输和潜在的恶意软件传播。以下是几种常见的禁用USB端口方法及其效果评价。 1、硬件方法&#xff1a; BIOS设置&#xff1a;通过BIO…