当前位置: 首页 > news >正文

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

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

相关文章:

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

技术群里的朋友遇到了这个问题&#xff0c;起初的原因是他对文件增加了一个属性配置 fileResult.EnableRangeProcessing true;这个属性我从未遇到过&#xff0c;然后&#xff0c;去F1查看这个属性的描述信息也依然少的可怜&#xff0c;只有简单的描述为(获取或设置为 启用范围…...

[Mac软件]Leech for Mac v3.2 - 轻量级mac下载工具

黑果魏叔推荐Leech是由Many Tricks开发的适用于Mac OS X的轻量级且功能强大的下载管理器。 Leech让您完全控制下载&#xff0c;并与浏览器完全集成。您可以将下载排队&#xff0c;暂停和恢复&#xff0c;从受密码保护的服务器下载&#xff0c;并将密码存储在系统范围的安全钥匙…...

留给“端侧大模型”的时间不多了

端侧大模型&#xff08;Edge AI models&#xff09;&#xff0c;也就是只在设备本地&#xff08;如智能手机、IoT设备、嵌入式系统等&#xff09;运行的大模型&#xff0c;过去一两年来非常流行。 具体表现在&#xff0c;终端设备厂商&#xff0c;如苹果、荣耀、小米、OV等&…...

Pytest框架中的Setup和Teardown功能

在 pytest 测试框架中&#xff0c;setup 和 teardown是用于在每个测试函数之前和之后执行设置和清理的动作&#xff0c;而pytest 实际上并没有内置的 setup 和 teardown 函数&#xff0c;而是使用了一些装饰器或钩子函数来实现类似的功能。 学习目录 钩子函数&#xff08;Hook…...

yolov10/v8 loss详解

v10出了就想看看它的loss设计有什么不同&#xff0c;看下来由于v8和v10的loss部分基本一致就放一起了。 v10的论文笔记&#xff0c;还没看的可以看看&#xff0c;初步尝试耗时确实有提升 好记性不如烂笔头&#xff0c;还是得记录一下&#xff0c;以免忘了&#xff0c;废话结束…...

Typescript高级: 深入理解infer关键字

概述 在 TS 中&#xff0c;infer 是一个高级类型操作&#xff0c;特别是条件类型和映射类型中非常有用的关键字它在泛型中使用也会是一个强大工具&#xff0c;增强了类型推断的能力&#xff0c;让开发者更灵活地处理和操作类型它允许在泛型类型推导过程中捕获一个具体的类型&a…...

JQC-3FF-S-Z 继电器模块使用(arduino)

前言 继电器模块可以控制电流的接通和非接通状态&#xff0c;和开关一样。实际上是用小电流去控制大电流运作的一种“自动开关” 本文只是简单使用继电器模块做一个 led 点亮和熄灭的案例&#xff0c;结合案例可以和 nodemcu 等板子结合做出远程控制开关。 材料准备 杜邦线…...

黑马一站制造数仓实战2

问题 DG连接问题 原理&#xff1a;JDBC&#xff1a;用Java代码连接数据库 Hive/SparkSQL&#xff1a;端口有区别 可以为同一个端口&#xff0c;只要不在同一台机器 项目&#xff1a;一台机器 HiveServer&#xff1a;10000 hiveserver.port 10000 SparkSQL&#xff1a;10001…...

网络I/O模型

网络I/O模型 同步I/O阻塞I/O非阻塞I/OI/O多路复用select函数接口示例 poll函数接口示例 poll 和 select 的区别epoll原理&#xff1a;示例 异步I/O 同步I/O 阻塞I/O 一个基本的C/S模型如下图所图&#xff1a;其中 listen()、connect()、write()、read() 都是阻塞I/O&#xff0…...

Docker 简介和安装

目录 Docker 是什么 跟普通虚拟机的对比 打包、分发、部署 Docker 部署的优势 Docker 通常用来做什么 重要概念&#xff1a;镜像、容器 安装 镜像加速源 Docker 是什么 Docker 是一个应用打包、分发、部署的工具 你也可以把它理解为一个轻量的虚拟机&#xff0c;它只虚…...

【源码】Spring Data JPA原理解析之Repository自定义方法命名规则执行原理(二)

Spring Data JPA系列 1、SpringBoot集成JPA及基本使用 2、Spring Data JPA Criteria查询、部分字段查询 3、Spring Data JPA数据批量插入、批量更新真的用对了吗 4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作 5、Spring Data JPA自定…...

Vue前端中从后端获取图片验证码

前端发送请求 <template><el-form :model"user" :rules"rules" ref"userForm" class"login" label-width"auto" style"max-width: 600px"><el-form-item label"用户名" prop"name…...

【源码】多语言H5聊天室/thinkphp多国语言即时通讯/H5聊天室源码/在线聊天/全开源

多语言聊天室系统&#xff0c;可当即时通讯用&#xff0c;系统默认无需注册即可进入群聊天&#xff0c;全开源 【海外聊天室】多语言H5聊天室/thinkphp多国语言即时通讯/H5聊天室源码/在线聊天/全开源 - 吾爱资源网...

gitlab 创建 ssh 和 token

文章目录 一、创建ssh key二、将密钥内容复制到gitlab三、创建token 一、创建ssh key 打开控制台cmd&#xff0c;执行命令 ssh-keygen -t rsa -C xxxxx xxxxx是你自己的邮箱 C:\Users\xx\.ssh 目录下会创建一个名为id_rsa.pub的文件&#xff0c;用记事本打开&#xff0c;并…...

Docker - Kafka

博文目录 文章目录 说明命令 说明 Docker Hub - bitnami/kafka Docker Hub - apache/kafka Kafka QuickStart Kafka 目前没有 Docker 官方镜像, 目前拉取次数最多的是 bitnami/kafka, Apache 提供的是 apache/kafka (更新最及时), 本文使用 bitnami/kafka bitnami/kafka 镜像…...

一键实现文件夹批量高效重命名:轻松运用随机一个字母命名,让文件管理焕然一新!

在数字化时代&#xff0c;文件夹管理是我们日常生活和工作中不可或缺的一部分。然而&#xff0c;随着文件数量的不断增加&#xff0c;文件夹命名的繁琐和重复成为了一个让人头疼的问题。你是否曾因为手动一个个重命名文件夹而感到枯燥乏味&#xff1f;你是否曾渴望有一种方法能…...

Vue3项目练习详细步骤(第二部分:主页面搭建)

主页面搭建 页面主体结构 路由 子路由 主页面搭建 页面主体结构 在vuews目录下新建Layout.vue文件 主页面内容主体代码 <script setup> import {Management,Promotion,UserFilled,User,Crop,EditPen,SwitchButton,CaretBottom } from element-plus/icons-vue imp…...

[个人总结]-java常用方法

1.获取项目根路径 user.dir是一个系统属性&#xff0c;表示用户当前的工作目录&#xff0c;大多数情况下&#xff0c;用户的当前工作目录就是java项目的根目录&#xff08;src文件的同级路径&#xff09; System.getProperty("user.dir") 结果&#xff1a;D:\code…...

什么是Java泛型?它有什么作用

Java泛型&#xff08;Generics&#xff09;是一种允许在定义类、接口和方法时使用类型参数的机制。泛型提供了一种机制&#xff0c;使得代码可以对多种类型的对象进行操作&#xff0c;而无需进行类型转换。 Java泛型的作用 类型安全&#xff1a;通过在编译时进行类型检查&…...

[机缘参悟-197] - 《道家-水木然人间清醒1》读书笔记 -21-看问题从现象到本质的层次

目录 1. 现象层&#xff1a; 2. 关联层&#xff1a; 3. 原因层&#xff1a; 4. 本质层&#xff1a; 5. 解决方案层&#xff1a; 6. 设计实现层&#xff1a; 7. 泛化&#xff1a; 8. 创新与发现&#xff1a; 看问题从现象到本质的层次是一个逐步深入、由表及里的过程。这…...

Prompt Tuning、P-Tuning、Prefix Tuning的区别

一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...

Springcloud:Eureka 高可用集群搭建实战(服务注册与发现的底层原理与避坑指南)

引言&#xff1a;为什么 Eureka 依然是存量系统的核心&#xff1f; 尽管 Nacos 等新注册中心崛起&#xff0c;但金融、电力等保守行业仍有大量系统运行在 Eureka 上。理解其高可用设计与自我保护机制&#xff0c;是保障分布式系统稳定的必修课。本文将手把手带你搭建生产级 Eur…...

GruntJS-前端自动化任务运行器从入门到实战

Grunt 完全指南&#xff1a;从入门到实战 一、Grunt 是什么&#xff1f; Grunt是一个基于 Node.js 的前端自动化任务运行器&#xff0c;主要用于自动化执行项目开发中重复性高的任务&#xff0c;例如文件压缩、代码编译、语法检查、单元测试、文件合并等。通过配置简洁的任务…...

HubSpot推出与ChatGPT的深度集成引发兴奋与担忧

上周三&#xff0c;HubSpot宣布已构建与ChatGPT的深度集成&#xff0c;这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋&#xff0c;但同时也存在一些关于数据安全的担忧。 许多网络声音声称&#xff0c;这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...

Spring Security 认证流程——补充

一、认证流程概述 Spring Security 的认证流程基于 过滤器链&#xff08;Filter Chain&#xff09;&#xff0c;核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤&#xff1a; 用户提交登录请求拦…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态

前言 在人工智能技术飞速发展的今天&#xff0c;深度学习与大模型技术已成为推动行业变革的核心驱动力&#xff0c;而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心&#xff0c;系统性地呈现了两部深度技术著作的精华&#xff1a;…...

ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]

报错信息&#xff1a;libc.so.6: cannot open shared object file: No such file or directory&#xff1a; #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...

实战设计模式之模板方法模式

概述 模板方法模式定义了一个操作中的算法骨架&#xff0c;并将某些步骤延迟到子类中实现。模板方法使得子类可以在不改变算法结构的前提下&#xff0c;重新定义算法中的某些步骤。简单来说&#xff0c;就是在一个方法中定义了要执行的步骤顺序或算法框架&#xff0c;但允许子类…...

Linux安全加固:从攻防视角构建系统免疫

Linux安全加固:从攻防视角构建系统免疫 构建坚不可摧的数字堡垒 引言:攻防对抗的新纪元 在日益复杂的网络威胁环境中,Linux系统安全已从被动防御转向主动免疫。2023年全球网络安全报告显示,高级持续性威胁(APT)攻击同比增长65%,平均入侵停留时间缩短至48小时。本章将从…...

Vue 3 + WebSocket 实战:公司通知实时推送功能详解

&#x1f4e2; Vue 3 WebSocket 实战&#xff1a;公司通知实时推送功能详解 &#x1f4cc; 收藏 点赞 关注&#xff0c;项目中要用到推送功能时就不怕找不到了&#xff01; 实时通知是企业系统中常见的功能&#xff0c;比如&#xff1a;管理员发布通知后&#xff0c;所有用户…...