如何实现文件断点续传功能

本文章首发于公众号攻城狮客栈,有需要的朋友可文末扫描二维码。

 

相信大家都使用过迅雷、电驴、百度云网盘等一类的工具,所有这些支持上传或下载的工具都有一个功能,那就是断点续传,也就是在你网络不佳传输断开时,传输会暂停,在网络恢复后,可以继续传输,从而避免数据的重复上传,以减少网络流量,提高效率。那么,你有仔细想过这其中的实现原理嘛?

PS:这两天没更,一是研究了下这个东西,二是偷懒去嗨了

 

我们之前利用rpc框架thrift做了一个远程通信的项目,具体是实现通过服务器对分布在各地的设备进行统一管理和控制,设备利用4G模块上网,因此,网络状况是影响稳定性的一个重要因素。由于之前客户端向服务器请求的文件较小,这种现象不是很明显,当客户说有需要传输1G左右大小的文件时,我们的系统就不得不做一个类似续传的功能了。

 

断点续传的原理

断点续传是由服务器给客户端一个已经上传的位置标记position,然后客户端再将文件指针移动到相应的position,通过输入流将文件剩余部分读出来传输给服务器

断点下载是由客户端告诉服务器已经下载的大小,然后服务器会将指针移动到相应的position,继续读出,把文件返回给客户端。 当然为了下载的更快一下,也可以多线程下载,那么基本实现就是给每个线程分配固定的字节的文件,分别去读

下面,我们以断点下载为例来进行详细说明。

通过上图我们看出,下载过程中,其实是客户端主动带着位置信息向服务端进行请求,主要分为以下几步:

  1. 准备:客户端获取需要下载的文件的相关信息

  2. 下载:携带特定请求大小信息进行下载请求

  3. 存储:对于小文件,可以直接进行存储,对于超大文件,由于内存限制,可以考虑采用存储多个小文件最后合并的方式,具体看业务需求即可。

  4. 续传:当网络或通信断开时,在程序内部记录响应的已存储大小信息,待网络正常后,继续下载即可;若是需要实现程序重启后续传,记录下载任务到相应的配置文件或数据库中即可。

有了具体的步骤之后,我们就来进行相应的编码设计吧,在编码过程中,对步骤进行相应的完善。

由于项目是利用rpc框架做成的,而且只能是客户端主动去向服务端发请求并获得响应,因此我这里通过一个客户端的C#程序来模拟了rpc通信的过程,以尽量贴合实际情况并使后期容易和项目进行集成,否则,光从设计和代码编写上来看,会觉得有许多无所谓的代码。

 

界面设计

我设计了一个简单的界面,来增加示例的直观程度,界面很简单,只包含简单的几项,但足以说明问题。

 

程序设计

代码编写上为了模拟实际情况,定义了一个server类、一个client类,server类只定义了一个获取数据的方法,用来模拟向客户端返回数据,client类包括模拟请求、开始下载以输出文件等方法。另外,在界面上通过定时器来定时刷新页面。

 

服务端

class Server
{string filePath;StreamReader reader;FileStream fileStream;BinaryReader binaryReader;public Server(string filePath){this.filePath = filePath;reader = new StreamReader(filePath);fileStream = new FileStream(filePath, FileMode.Open,FileAccess.Read);binaryReader = new BinaryReader(fileStream);}public byte[] ReturnFile(int start,int length){int size = fileStream.Length-1 - long.Parse(start.ToString()) >= length ? length : Convert.ToInt32(fileStream.Length-1 - long.Parse(start.ToString()));byte[] b = new byte[size];//FileStream fileStream = new FileStream(filePath, FileMode.Open);//BinaryReader binaryReader = new BinaryReader(fileStream);binaryReader.Read(b, 0, size);return b;}
}

 

客户端

我们主要来分析一下客户端。

客户端在下载时,需要知晓对应的文件、文件大小信息等,而且为了续传还需要知道已下载部分的大小,另外,由于文件可能过大,所以除了定义每次请求的大小外,还需要定义每个小文件的大小,这2个配置最好是写在配置文件里,虽然我这里并没有这样做。为了避免频繁的io,我们在达到一个小文件大小时进行一次写入操作,因此,在这之前的数据都只能暂时存在内存中,因此还需要定义一个数据块的集合,如下,就是上述列出的一部分属性和字段定义。

public string FilePath { get; set; }
public string FileName { get; set; }
public int TotalSize { get; set; }
public int DownloadSize { get; set; }
public int StartPosition { get; set; } = 0;private int blockLength = 1024*1024;//1M每个块大小
private int blockCount = 10;//每个小文件的大小List<byte[]> datas = new List<byte[]>();

说完了定义,我们来看一下方法,首先,是我们的下载方法,在未下载完成以及未断开时,应一直处于下载状态,下载过程中,包括数据请求,数据拼接,数据写入以及状态更新等。

public void DownloadFile()
{isPause = !isPause;while (!isComplete&&!isPause){byte[] b = GetBlock();datas.Add(b);DownloadSize += b.Length;if (DownloadSize== TotalSize){isComplete = true;}if (datas.Count == blockCount|| isComplete){w2f();}StartPosition= DownloadSize-1;if (isComplete){//isComplete = true;//w2f();//StartPosition = 0;break;}}if (isComplete){StartPosition = 0;}
}private void w2f()
{fileNum++;//byte[] bt = new byte[blockLength * datas.Count];byte[] bt = new byte[datas.Sum(o => o.Length)] ;datas.ForEach(o => bt.CopyTo(o, 0));//string name = FileName + fileNum;//File.WriteAllBytes(name, bt);//FileStream fileStream = new FileStream(name, FileMode.Append, FileAccess.Write);fileStream.Write(bt, 0, bt.Length);fileStream.Flush();datas.Clear();
}
上面的w2f方法,大家可以看到,我并不是按照小文件合并的方式来实现的,但注释部分已经实现了该操作,只是有没实现合并,但原理和这里是相同的。

界面部分

界面部分,主要是通过定时器来刷新界面,以得知当前的下载情况,可以做一个下载进度的功能。另外,可以通过暂停来模拟断开并再次点击模拟恢复的情况。

public Form1()
{InitializeComponent();btn_Pause.Enabled = false;timer = new System.Windows.Forms.Timer();timer.Tick += Timer_Tick;timer.Interval = 2000;
}private void Timer_Tick(object sender, EventArgs e)
{lb_download.Invoke(new Action(() => { lb_download.Text = client.DownloadSize.ToString()+" kb"; }));double process = client.DownloadSize / fileSize * 100;lb_download.Invoke(new Action(() => { lb_process.Text = process.ToString() + " %"; }));
}

到这里的话,基本上功能就已经实现的差不多了,但仍然有需要完善的地方,比如,文件下载完成后,按道理肯定是需要做一个校验以确保文件正确性的,而且,像迅雷这种软件,在下载时,需要用到种子,其实他里边就存储有校验相关的信息,而且不只是校验整个文件,他会对每个小块都进行校验,如果不匹配就进行相应操作。具体的实现的话,其实也很简单,就是客户端拿到要下载的文件时,把相应的校验消息也拿到,等下载完成后进行匹配即可。

我这里因为使用了rpc框架,框架自身带有对异常的处理机制,因此,在整体上看,只要下载的文件大小最终是匹配的,那出现错误的概率还是很小的,另外,在实际的项目中,已自主实现了心跳以及重连的功能,因此可以在重连后调用续传的方法即可。

最后,还要说的一点是,网页版的实现原理也是这样,但是实现方式不同,http协议头里可以添加请求相关的信息,因此可通过构建http头的方式来向服务端发送请求实现类似的功能,并且,现在很多主流的前端框架都支持文件的分块上传,极大的便利了该功能的实现,有兴趣的同学可以自己研究一下。

 

本章源码可访问

https://github.com/IronMarmot/Samples/tree/master/BreakpointResumeDemo

 

更多精彩内容,请微信搜索攻城狮客栈 或扫描下方二维码

------------------------------------------------------------------------------

公众号:攻城狮客栈

CSDN:画鸡蛋的不止达芬奇

 

                                                                 

让我们一起变的更优秀。


 

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

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

相关文章

python 断点续传

python 断点续传 1.前序2.技术原理2.1 Content-Range2.2 Range 3. 代码实现 1.前序 当下载突然断开后&#xff0c;断点续传就需要了&#xff0c;继续前面下载的内容下载。解决了不需要重复下载 2.技术原理 HTTP/1.1 开始支持断点续传&#xff0c;一般断点下载会用到 Range 和…

web前端Tips:断点续传如何实现?

在Web前端中实现断点续传功能的一种常见方式是使用HTTP Range请求和文件分片上传。 以下是一个简单的断点续传实现的步骤&#xff1a; 前端将要上传的文件分成多个固定大小的片段&#xff08;chunk&#xff09;&#xff0c;例如每个片段的大小为1MB。当用户选择上传文件时&am…

前段实现文件的断点续传

早就听说过断点续传这种东西&#xff0c;前端也可以实现一下 断点续传在前端的实现主要依赖着HTML5的新特性&#xff0c;所以一般来说在老旧浏览器上支持度是不高的 本文通过断点续传的简单例子&#xff08;前端文件提交后端PHP文件接收&#xff09;&#xff0c;理解其大致的实…

Android开发——断点续传原理以及实现

0. 前言 在Android开发中&#xff0c;断点续传听起来挺容易&#xff0c;在下载一个文件时点击暂停任务暂停&#xff0c;点击开始会继续下载文件。但是真正实现起来知识点还是蛮多的&#xff0c;因此今天有时间实现了一下&#xff0c;并进行记录。本文原创&#xff0c;转载请注…

如何实现断点续传

断点续传是指在网络传输中&#xff0c;当传输过程中出现异常或者用户主动停止传输时&#xff0c;能够恢复传输过程&#xff0c;避免重新传输已经传输过的数据&#xff0c;提高传输效率。实现断点续传可以通过以下方式&#xff1a; HTTP协议支持的断点续传 在HTTP协议中&#xf…

【MQ学习笔记】RocketMQ知识分析与总结

RocketMQ 为什么使用mq&#xff1f;mq的作用&#xff1f;mq对你项目带来了什么&#xff1f;不选mq行不行&#xff1f; 异步&#xff0c;MQ能够以异步的方式对消息进行处理&#xff0c;能够大大提高了系统的响应以及吞吐量解耦&#xff0c;MQ双方只需要负责生产或消费信息即可…

销售数据分析方法、如何写好一个专题分析报告、Hive大数据知识体系教程、大数据分析平台总体架构方案……| 本周精华...

▲点击上方卡片关注我&#xff0c;回复“8”&#xff0c;加入数据分析领地&#xff0c;一起学习数据分析&#xff0c;持续更新数据分析学习路径相关资料~&#xff08;精彩数据观点、学习资料、数据课程分享、读书会、分享会等你一起来乘风破浪~&#xff09;回复“小飞象”&…

老杨刷完了23个跨年演讲,这6场最适合网工

晚上好&#xff0c;我是老杨。 放假放了一阵子&#xff0c;老杨闲着没事儿&#xff0c;一共刷了23个跨年演讲。 好的演讲&#xff0c;和一本好书一样&#xff0c;都是可以震荡灵魂的。 也知道最近大家都忙得很&#xff0c;这里推荐7个最值得一听的跨年演讲&#xff0c;贴心吧…

朱广权李佳琦直播掉线,1.2 亿人在线等

作者 | 胡巍巍 出品 | 程序人生&#xff08;ID&#xff1a;coder_life&#xff09; 原来央视爸爸也有掉线的时候。 4月6日晚间&#xff0c;“国民段子手”朱广权连麦李佳琦&#xff0c;给湖北做公益带货直播。 “小朱配琦”的神仙组合&#xff0c;让人大呼过瘾&#xff0c;该直…

“我让 AI 来处理我 24 小时的音频信息——这会是未来吗?”

整理 | 郑丽媛 出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09; 这几年 AI 发展和进化的速度&#xff0c;几乎可以用三个字来形容&#xff1a;杀疯了。 AI 下棋、AI 编程、AI 作画、AI 写小说、AI 预测蛋白质结构……当 AI 逐渐融入我们的生活&#xff0c;你是否设…

抗住百万人直播、被联合国推荐,起底飞书技术演进之路!

你去公司上班了吗&#xff1f; 随着近期接连不断传来的好消息&#xff0c;上班族开始关心起这问题来。但许多企业在相关政策的号召下&#xff0c;仍采取远程办公的方式。据艾媒咨询的数据显示&#xff0c;今年春节期间中国远程办公企业规模超过 1800 万家&#xff0c;远程办公人…

听8位淘宝工程师聊聊他们眼中的元宇宙 | 1024特辑

我们是技术工作者&#xff0c;致力于思考和创新&#xff0c;用代码去解决生活中的问题&#xff0c;为消费者的快乐和幸福而努力。 今天是1024程序员节&#xff0c;我们邀请了8位不同岗位的淘宝工程师&#xff0c;聊了聊他们眼中的未来消费生活&#xff0c;他们眼中的元宇宙&…

数字人的新革命,BAT的“冲高”战场

配图来自Canva可画 ChatGPT横空出世&#xff0c;让人们看到了数字人的另一种可能&#xff0c;将ChatGPT与虚拟数字人融合&#xff0c;研发出更加智能化、拟人化的虚拟数字人成为数字人厂商的新命题、新方向。 2月份&#xff0c;岭南股份、风语筑、开普云等10多家公司&#xf…

数字化直播沟通

目录 前言 一、介绍数字化&#xff08;5-10分钟&#xff09; 主播提问&#xff08;王&#xff09;&#xff1a; 回答&#xff08;向&#xff09;&#xff1a; 什么是数字化&#xff1f; 好的&#xff0c;我们来看下一个问题&#xff1a;什么是数字化转型&#xff1f; 好的…

部署React项目到云服务器(步骤清晰)

部署React项目到云服务器 前言(下载相关软件)开始部署服务器以及域名购买实例远程连接 或 XShell 7 远程连接服务器XShell 7 连接云服务器操作使用FileZilla将 打包后的 build(可能你们那里打包是dist)文件塞到服务器文件夹感谢看到这里&#xff01;点个关注再走啊&#xff01;…

云服务安装配置git

搭建网站&#xff0c;我们有时候是直接从github拉取代码&#xff0c;这就要配置git。 云服务器搭建网站还需要一些其他的基础配置&#xff0c;前面我们已经讲到&#xff0c;未进行配置的可以参考之前的文章。 云服务基本配置&#xff1a;腾讯云服务器小白保姆级教程_大鹏bmfm…

【回答问题】ChatGPT上线了!写出coco数据集的图像分类训练和预测函数代码?

写出coco数据集的图像分类训练代码 好的,我会尽力写出图像分类训练代码。首先,你需要准备好 COCO 数据集的图像和标签,并将其转换成适合你模型使用的格式。其次,你需要选择一个图像分类模型,并定义好模型的结构。接下来,你可以使用 PyTorch 等深度学习框架来实现训练代码…

服务器访问次数限制实现的思路

有个每分钟只能访问10次接口的需求&#xff0c;想到2个实现方法分享给大家&#xff0c;希望可以一起进步&#xff0c;一开始的思路是记录下用户访问的时间和次数&#xff0c;然后在写一些方法实现。最先想到是记录在数据库里&#xff0c;刚好数据库有记录访问日志的表&#xff…

若登陆账号错误次数过多则限制登陆一定时长(自定义时长)

前提须知:登陆拦截器此演示:登陆5次错误 则限制登陆时长5秒 业务层实现方法 在控制器中使用此方法,若判断用户账户和密码是否正确则返回用户对象定义session中的key为’count’定义一个日期对象存放时间,时间是当前时间的后5秒,将此时间存放进session中key为’time’中若登陆…

根据IP限制指定时间内访问接口的次数

在网上看见有人问一个问题&#xff1a;想限制一下某个接口在一分钟之内只能被同一个ip请求指定次数。 方法比较多&#xff0c;这里就用Redis做一个简单的限制。 大致逻辑&#xff1a; 把请求的ip作为key,请求次数作为value存储在Redis里面&#xff0c;第一次请求value为1&am…