用C实现断点续传的功能,详细点的实现原理是啥嘞
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用C实现断点续传的功能,详细点的实现原理是啥嘞相关的知识,希望对你有一定的参考价值。
参考技术A用C实现断点续传的功能,详细点的实现原理是什么嘞
于HTTP 协议的多执行绪下载和断点续传的实现 学 生:叶升路 指导教师:覃 颖 (三峡大学 电气资讯学院) 摘 要:本文介绍了网路下载软体中的最新技术——多执行绪下载和断点续传技术,同时 也介绍了HTTP 协议的发展、特点以及WinSock 程式设计技术。
FTP客户端怎么样实现断点续传的功能?
上传的时候,如果档案已经存在,服务端会返回档案已经存在大小,根据这个大小,从本地档案读取这个大小之后的资料,进行断点续传
c# winform 中怎么利用wini实现断点续传?
WinI 提供对常用的网际网路协议,包括 Gopher、 FTP 和 HTTP 访问。使用 WinI,可以不必处理 WinSock、 TCP/IP 或特定的网际网路协议的详细资讯写入 Inter 客户端应用程式的程式设计中,更高的级别。
FTP 通常公开要将一个档案附加到另一个"追加"命令。WinI 不直接公开此功能。
在 Inter Explorer 3.0 和 4.0 中,这一问题的解决方法是从伺服器下载档案、 将附加到该客户端然后上载该档案备份到伺服器上。
从开始 Inter Explorer 5,FTP 命令可以直接传送到 FTP 伺服器使用 FtpCommand,如下所示:
CHAR szTemp[256];
wsprintf (szTemp, "APPE %s", "DestFile.txt");
bRet = FtpCommand( hConnection, WinI Connection handle
TRUE, Yes, I expect a response
FTP_TRANSFER_TYPE_ASCII, I\'m receiving ASCII
szTemp, This is the FTP mand I am passing
0, No context needed
&hResponse); The handle to read the response
if (!bRet)
cout << "FtpCommand failed, error: " << GetLastError() << endl;
return;
wsprintf (szTemp, "This data will be appended to the file");
DWORD dwSize;
if (!InterWriteFile (hResponse, (LPVOID)szTemp, lstrlen(szTemp)+1, &dwSize))
cout << "InterWriteFile failed, error: " << GetLastError() << endl;
return;
用swoole做websocket,怎么实现资料断点续传
在php的web控制器中,每当更新了资料库的资料库后,我需要将这些资料传送到其他使用者的客户端, 不知在web控制器中怎么实现这个一个目的,怎么才能swoole_websocket_server传送这些最新的资料。如果再建立一个 swoole_client话又感觉不好。
主轴定位功能的实现原理是什么
主轴定位指通过一个输出点控制主轴旋转到特定的位置以方便换刀或者主轴配合其它装置动作。
主轴定位是通过主轴伺服驱动模组内建的位置控制功能和检测主轴的位置编码器(可以安装在主轴上或者是主轴电机内建的编码器)来实现主轴定位的。
API有断点续传的功能吗?
有,可以通过html5 File api 实现断点续传。
一、实现档案多选
HTML5的<input>新增了"multiple"属性,该属性可接受多个值的档案上传栏位
- <input type=file multiple="multiple" name="file"id="file">
添加了该属性使用者就可以在弹出的对话方块中一次性选择多个档案了
二、实现档案从计算机拖拽到网页以及新增档案伫列功能
这里我们用 dragover 和 drop 两个事件来管理档案拖拽的功能
其中 dragover 用来处理在指定的元素上移动时的事件,这里我们通过给body系结dragover时间来处理页面中拖动档案的事件
- document.body.addEventListener(\'dragover\', dragFile,false); function dragFile(evt) evt.sPropagation(); evt.preventDefault(); evt.dataTransfer.dropEffect = \'copy\';
用 drop 事件来处理滑鼠松开时候的事件,此时应该将使用者拖动过来的档案加入到上传伫列中,以供后续的处理
- document.body.addEventListener(\'drop\', dropFile,false); function dragFile(evt) evt.sPropagation(); evt.preventDefault(); dataTransfer.files属性可以获取到所有拖动选择的档案,通过遍历可以读取到所有档案的资讯。 遍历每个档案可以获取到档案的 name、size、type、lastModifiedDate等关键资讯 var files = evt.dataTransfer.files; addfile 方法 用来新增上传档案伫列,在input的change事件中也需要呼叫 该方法首先检查有无档案正在上传中,如果有就将后续加入的档案放到上传伫列中,如果没有档案正在上传就直接执行上传命令 addfile(files);
断点续传指的是在下载或上传时,将下载或上传任务(一个档案或一个压缩包)人为的划分为几个部分,每一个部分采用一个执行绪进行上传或下载,如果碰到网路故障,可以从已经上传或下载的部分开始继续上传下载未完成的部分,而没有必要从头开始上传下载。使用者可以节省时间,提高速度。
360浏览器如何实现断点续传
- 360安全浏览器内建下载工具是支援断点续传功能的,但是断点续传需要下载工具和下载资源相互支援配合才行。
所以,如果使用360se下载的档案无法断点续传的话,一般都是资源伺服器不支援断点续传功能。
部分下载网为了防止盗链是不允许断点续传的。
代理的实现原理是什么?
代理伺服器有很多种,大体分为三类:HTTP、FTP、SOCKS,其中又分为透明代理和不透明代理,透明代理一般是闸道器,为硬体
过程:
(1)客户端先和代理伺服器通讯,建立TCP连线,目的IP是代理伺服器的IP
(2)客户端发出GET命令,GET命令中包含URL或IP地址、明文
(3)代理伺服器将其中的URL转换为IP地址,可能会有DNS,将源资料包中的资料拷贝下来,去掉URL,重新组包再发出去
(4)代理伺服器和真实伺服器通讯,源IP是代理伺服器的IP
以上就是代理的实现原理,在我们日常生活中只有在学习IT才会了解哦,我也是在黑马程式设计师学习之后才了解的。
什么是ECMP?ECMP的实现原理是什么?
Linux 平台上之 Multipath Routing 应用
--------------------------------------------------------------------------------
撰稿/整理: Rex Tsai <chihchun@kalug.linux.>
HTML 版本:
:kalug.linux./sections.php?op=viewarticle&artid=4
TEXT 版本:
[Tips] Linux 平台上之 Multipath Routing 应用.
$Date: 2001/10/22 09:25:41 $
警告:
*****************************************************************************
您因使用或不能使用本文之教学而产生之任何软体、硬体损害(包括但不限
于直接或间接个人损害、营业利润之丧失、业务中断、营业资讯之遗失、或
任何其他金钱损失),概不负任何损害赔偿责任。
The author has taken care in the preparation of this paper, but
make no expressed or implied warranty of any kind and assume no
responsibility for errors or omissions. No liability is assumed for
incidental or consequential damages in connection with or arising
out of the use of the information or programs contained herein.
Linux is a registered trademark of Linus Torvalds. Other product
and pany names mentioned herein may be the trademarks of their
respective owners.
*****************************************************************************
撰稿/整理: Rex Tsai <chihchun@kalug.linux.>
此档案应有许多技术上得问题. 请熟悉的朋友来信指教.
[Introduction]
许多中小企业或是 Soho 甚至家庭, 会在家中装有一条以上宽频/宽频线路.
这篇 Tips 将介绍如何在 Linux 上, 好好的应用这些资源.
在你使用两家不同的 ISP 的时候, destination address-based load
balancing 将会是最好的解决方案. 你的封包可以即时透过不同的 ISP 线路
走出去. 当然这里所提的是从内部到外部, 如果你的专线提供不同的网路服
务提供给网路上得人存取, 那么接下来所说明的方式不适合您.
此篇文章只能提供内部网路对外的 load sharing.
以下文章以 GNU/Debian, Linux kernel 2.4.12, iproute-20001007-1 为
环境, 所有动作请在主机前面执行.
假设你有两条以上的网际网路资料线路 (xDSL, ISDN, Cable, whatever..),
想充分使用这些线路, 概观来看有三种方式
1. Multiplexing
2. Packet-wise load balancing
3. Destination address-based load balancing,
或称 Equal-Cost MultiPath Routing (ECMP)
稍微解释一下三种方式的不同
1. Multiplexing
Multiplexing, 这个方式提供某些路由器提供 offer-load balancing 或叫
做 load sharing. 这个方式可以让路由器将流量分给不同的外流 ports.
但是会造成每个 port 的传送上约有 30 % 的 overhead. 此外, 每家厂商的
实作都是独一的, 因次你会被锁在特定的解决方案上.
类似的技术是 "bonding" 或是 "multi-link". 这里所提得 Bonding 是一
个标准, 是由 Bandwidth on Demand Interoperability Group (一个大
概有四十个制造商的协会)所提. 已经提交给 American National Standards
Institute TR41.4 group. 这个通常的在于两条资料线路都是接在同一个
点(ISP)上的时候, 因此如果是两条不同 ISP 所提供的线路, 那么就没办法
达到这个目的. 当然, 如果你的 ISP 不提供此项技术服务, 那么也是没辙.
举个例子, 像是 stick multiple ISDN channels. 将几个慢速的线路合成为
快速的一条. 像是 ISDN H.221 规格即用到 inverse multiplexing. 但通常
用于视讯传输而不是电子资料.
2. Packet-wise load balancing:
这个在你可以得到所有的 ISP 协助的时候是可行的, 如果两家 ISP 都愿意
协助将其不同的路由器皆设定到同一个 IP 位址. 那么便可以这么作.
在这个方式中, 你会用到像是 sch_teql (the TEQL scheduler) 来创造一个
virtual device 将你的封包分散在不同的网路介面上.
一般来讲, 如果你是使用两家不同 ISP , 那么你不可以使用这个解决方案.
但, 你 "可以" 将所有的网路封包透过 IPIP or CIPE (Crypto IP Encapsu-
lation) 的方式来解决这个问题. 看看 Linux Kernel 中的 IP: tunneling.
3. Equal-Cost MultiPath:
在 Linux 核心中叫做 equal cost multipath (CONFIG_IP_ROUTE_MULTIPATH)
比较正确的说法应该是 "destination address-based load balancing". 一
般 Linux 想要为某个 IP 位址找到路由, 会因为效能的问题去查验暂存(cache)
中的资料, 如果目标 IP 并没有在暂存空间中, 那么他便会去查 routing table
来决定该 IP 位址的路由, 并将该路由放进 cache 中.
一般来讲核心中的路由功能只能为某个封包决定唯一的方向. 如果使用 ECMP 并
有机会让某个 package pattern 具有好几种不同的方向, 可以让某个符合路由条
件的封包透过 "equal" cost 或是自订的权重来选择该走的路由.
[Howto]
如果你有数台电脑想使用外部网路, 一条 ADSL 不够你使用(例如某人抓档太凶)
那么这是你正在找的解决方案. 你所能做的作好的方式就是每个 connection 可以
以 "non-deterministic fashion" 的方式选择路由, 将 connection 分散到不同的
Router 上, 注意: 这里所说的不是 packets, 这样 TCP/IP session 将无法连续.
但是这整个路由程式有两个部份四个问题要解决.
1a. How to get your packets to the outside world,
1b. How the outside world replies to you,
2a. How the outside world sends packets to you,
2b. How you reply to the outside world.
Multipath 可以解决 1a 出去的问题. 一般的路由设定可以解决 1b 与 2a. 而 2b
则必须使用 policy routing (multiple tables)才能解决, Multiple Tables 可以
让你加入以封包来源位址为依据来决定路由.
以下的范例, 在核心中, 务必加入编译以下选项
必备:
CONFIG_NETLINK=y
这个选项是 Kernel/User link socket
CONFIG_RTNETLINK=y
Routing messages
CONFIG_INET=y
TCP/IP neorking
CONFIG_IP_ADVANCED_ROUTER=y
IP: advanced router
CONFIG_IP_MULTIPLE_TABLES=y
IP: policy routing
CONFIG_IP_ROUTE_MULTIPATH=y
IP: equal cost multipath
选用:
CONFIG_IP_ROUTE_LARGE_TABLES=y
一般来讲 IP: large routing tables 也会勾选, 一方面 routing
zones 可以大于 64 笔, 这些资料存在 hash 资料结构中, 也可以
加速 "the routing process".
而 iproute 套件也是必须的软体. 这个软体的安装方式与位置请洽询提供
您所使用套件之厂商/组织. (Red Hat, Debian, Mandrake, SuSE, etc...)
重头戏来了, 这里假设你有三块网路卡, 分别给内部网路与两家 ISP. eth0
是内部网路, eth1 与 eth2 是其他两家 ISP 线路.
eth0 是内部网路, 范围是 10.0.0.0/255.255.255.0
eth1 其中一家 ISP, IP 是 1.1.1.1, 闸道器(gateway)是 1.1.1.253
eth2 另外一家 ISP, IP 是 2.2.2.2, 闸道器(gateway)是 2.2.2.253
# 列出所有的 rule
ip rule list
# table 后的 "10" 是 table identifer, 为数字.
# 注: 可用英文代称取代请看 /etc/iproute2/rt_tables
#
# table 10 是给在 gateway 后面的内部网路使用, 10.0.0.x 是 LAN 使用的 IP.
#
# pref 后面指定的 "10" 是 priority. 为 policy routing database 搜寻的次序
ip rule add pref 10 to 10.0.0.0/24 table 10
ip route add 10.0.0.0/24 table 10 dev eth1
# table 20 给 ISP #1, IP 1.1.1.1, gateway 1.1.1.253
# pref 后面指定的 "20" 是 priority. 为 policy routing database 搜寻的次序
ip rule add pref 20 from 1.1.1.1 table 20
ip route add default table 20 via 1.1.1.253
# table 30 is for ISP #2, IP 2.2.2.2, gateway 2.2.2.253
ip rule add pref 20 from 2.2.2.2 table 30
ip route add default table 30 via 2.2.2.253
# 列出所有的 rule
ip rule list
# 列出 table 10 的 rule
ip route list table 10
# 列出 table 20 的 rule
ip route list table 20
# If your ISP\'s have servers that authenticate by originating IP address,
# (e.g. SMTP or NNTP servers) you will want to explicitly list them here.
# 这里是静态的 routing table 设定. 如果你的 ISP 有提供某些网路服务, 必须该
# ISP 的 IP 才能使用, 那么你会想将它设定在这里
# (e.g. Proxy, SMTP or NNTP Server)
ip route add 1.1.1.0/24 dev eth1
ip route add 2.2.2.0/24 dev eth2
# 如果上面所有的 routing table 都没有吻合, 那么封包会走 default route
# 这里使用 "ECMP" 来选择上游路由器.
# "ip route repleace" 是用来取代原本的 default routi.
ip route replace default nexthop via 1.1.1.253 dev eth1 \\
nexthop via 2.2.253 dev eth2
# 如果你想加上权重, 是这样使用的. 请依据你的线路网路频宽
# 频宽越大, 请把 weight 加大.
#ip route replace default nexthop via 1.1.1.253 dev eth1 weight 1 \\
# nexthop via 2.2.253 dev eth2 weight 3
# Make it all happen. IMPORTANT! The above mands do NOT
# flush the route cache!
ip route flush cache
<-- 结束 -->
Linux 上得 ECMP implementation 的实作有一个特色, 当你的上游网路介面
使用 ARP (e.g. 使用传统 IP-over-Ether)时, 如果其中一个网路介面阵
亡时, Linux kernel 会自动把该介面 "shutdown", 并停止那些需要经过该
介面的 "nexthops". 但是要附注一提的是, 如果你使用的是 "网路型" ADSL
服务, 你会取得一个 ADSL 路由器, 你可能会架设一个 NAT 伺服器与 ADSL
路由器放在同一个 Ether Hub 上, 如果这个时候 ADSL 断线, 但是 Hub
并未断线, Linux 会认为该网路卡仍在 on-line 状态. 除非是该 hub 或是网
路卡故障, kernel 才会 shutdown 该网路介面.
因此如果你需要确实的 "failover", 请动手写一个小 script, 定时用 ping
或称 "icmp echo request" 来询问 ISP 端的闸道器是否正常的运作, 并用
"ip route replace" 来置换那些确定可通的路由. 这里就请您自行处理了.
Enjoy it, Rex.
[Reference]
[1] Linux-Net mailing list. :uwsg.iu.edu/hypermail/linux//
原文 :uwsg.iu.edu/hypermail/linux//0107.3/0028.
[2] Alexey N. Kuzsov, April 14, 1999, IP Command Reference
[Futrher reading]
[1] Jack Coates , Load-Balancing on LRP HOWTO
[2] Netherlabs BV (bert hubert ),
Gregory Maxwell ,
Remco van Mook ,
Martijn van Oosterhout ,
Paul B Schroeder ,
Jasper Spaans , howto@ds9a.nl,
Linux 2.4 Advanced Routing Howto
[3] Horacio J. Pe?a, horape@pendium..ar, 05/Apr/2000
Policy based routing MICRO-HOWTO,
:pendium..ar/policy-routing.txt
[Keywords]
Linux, Routeing, 路由, ADSL, 宽频, 宽频, 频宽, 合并, 分流, 共用
[Acknowledgements]
Zygo Blaxell (zblaxell@furryterror.) 在 Linux- 的说明
Digital Sesame, Inc - :d11e. 提供环境测试.
--
Best Regards.
Rex Tsai <chihchun_at_kalug.linux.>
--
ThinkPHP 的 C 函式实现原理是什么?
c方法里面有个静态变数,每次初始化的时候就把配置赋值给这个静态变数,然后每次使用的时候就直接在这个静态变数里获取了,楼主应该知道静态变数在整个执行期间都存在吧
很简单的Java断点续传实现原理
原理解析
在开发当中,“断点续传”这种功能很实用和常见,听上去也是比较有“逼格”的感觉。所以通常我们都有兴趣去研究研究这种功能是如何实现的?
以Java来说,网络上也能找到不少关于实现类似功能的资料。但是呢,大多数都是举个Demo然后贴出源码,真正对其实现原理有详细的说明很少。
于是我们在最初接触的时候,很可能就是直接Crtl + C/V代码,然后捣鼓捣鼓,然而最终也能把效果弄出来。但初学时这样做其实很显然是有好有坏的。
好处在于,源码很多,解释很少;如果我们肯下功夫,针对于别人贴出的代码里那些自己不明白的东西去查资料,去钻研。最终多半会收获颇丰。
坏处也很明显:作为初学者,面对一大堆的源码,感觉好多东西都很陌生,就很容易望而生畏。即使最终大致了解了用法,但也不一定明白实现原理。
我们今天就一起从最基本的角度切入,来看看所谓的“断点续传”这个东西是不是真的如此“高逼格”。
其实在接触一件新的“事物”的时候,将它拟化成一些我们本身比较熟悉的事物,来参照和对比着学习。通常会事半功倍。
如果我们刚接触“断点续传”这个概念,肯定很难说清楚个一二三。那么,“玩游戏”我们肯定不会陌生。
OK,那就假设我们现在有一款“通关制的RPG游戏”。想想我们在玩这类游戏时通常会怎么做?
很明显,第一天我们浴血奋战,大杀四方,假设终于来到了第四关。虽然激战正酣,但一看墙上的时钟,已经凌晨12点,该睡觉了。
这个时候就很尴尬了,为了能够在下一次玩的时候,顺利接轨上我们本次游戏的进度,我们应该怎么办呢?
很简单,我们不关掉游戏,直接去睡觉,第二天再接着玩呗。这样是可以,但似乎总觉着有哪里让人不爽。
那么,这个时候,如果这个游戏有一个功能叫做“存档”,就很关键了。我们直接选择存档,输入存档名“第四关”,然后就可以关闭游戏了。
等到下次进行游戏时,我们直接找到“第四关”这个存档,然后进行读档,就可以接着进行游戏了。
这个时候,所谓的“断点续传”就很好理解了。我们顺着我们之前“玩游戏”的思路来理一下:
假设,现在有一个文件需要我们进行下载,当我们下载了一部分的时候,出现情况了,比如:电脑死机、没电、网络中断等等。
其实这就好比我们之前玩游戏玩着玩着,突然12点需要去睡觉休息了是一个道理。OK,那么这个时候的情况是:
- 如果游戏不能存档,那么则意味着我们下次游戏的时候,这次已经通过的4关的进度将会丢失,无法接档。
- 对应的,如果“下载”的行为无法记录本次下载的一个进度。那么,当我们再次下载这个文件也就只能从头来过。
话到这里,其实我们已经发现了,对于我们以上所说的行为,关键就在于一个字“续”!
而我们要实现让一种断开的行为“续”起来的目的,关键就在于要有“介质”能够记录和读取行为出现”中断”的这个节点的信息。
转化到编程世界
实际上这就是“断点续传”最最基础的原理,用大白话说就是:我们要在下载行为出现中断的时候,记录下中断的位置信息,然后在下次行为中读取。
有了这个位置信息之后,想想我们该怎么做。是的,很简单,在新的下载行为开始的时候,直接从记录的这个位置开始下载内容,而不再从头开始。
好吧,我们用大白话掰扯了这么久的原理,开始觉得无聊了。那么我们现在最后总结一下,然后就来看看我们应该怎么把原理转换到编程世界中去。
- 当“上传(下载)的行为”出现中断,我们需要记录本次上传(下载)的位置(position)。
- 当“续”这一行为开始,我们直接跳转到postion处继续上传(下载)的行为。
显然问题的关键就在于所谓的“position”,以我们举的“通关游戏来说”,可以用“第几关”来作为这个position的单位。
那么转换到所谓的“断点续传”,我们该使用什么来衡量“position”呢?很显然,回归二进制,因为这里的本质无非就是文件的读写。
那么剩下的工作就很简单了,先是记录position,这似乎都没什么值得说的,因为只是数据的持久化而已(内存,文件,数据库),我们有很多方式。
另一个关键在于当“续传”的行为开始,我们需要需要从上次记录的position位置开始读写操作,所以我们需要一个类似于“指针”功能的东西。
我们当然也可以自己想办法去实现这样一个“指针”,但高兴地是,Java已经为我们提供了这样的一个类,那就是RandomAccessFile。
这个类的功能从名字就很直观的体现了,能够随机的去访问文件。我们看一下API文档中对该类的说明:
此类的实例支持对随机访问文件的读取和写入。随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组。
如果随机访问文件以读取/写入模式创建,则输出操作也可用;输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。
写入隐含数组的当前末尾之后的输出操作导致该数组扩展。该文件指针可以通过 getFilePointer 方法读取,并通过 seek 方法设置。
看完API说明,我们笑了,是的,这不正是我们要的吗?那好吧,我们磨刀磨了这么久了,还不去砍砍柴吗?
实例演示
既然是针对于文件的“断点续传”,那么很明显,我们先搞一个文件出来。也许音频文件,图像文件什么的看上去会更上档次一点。
但我们已经说了,在计算机大兄弟眼中,它们最终都将回归“二进制”。所以我们这里就创建一个简单的”txt”文件,因为txt更利于理解。
我们在D盘的根目录下创建一个名为”test.txt”的文件,文件内容很简单,如图所示:
没错,我们输入的内容就是简单的6个英语字母。然后我们右键→属性:
我们看到,文件现在的大小是6个字节。这也就是为什么我们说,所有的东西到最后还是离不开“二进制”。
是的,我们都明白,因为我们输入了6个英文字母,而1个英文字母将占据的存储空间是1个字节(即8个比特位)。
目前为止,我们看到的都很无聊,因为这基本等于废话,稍微有计算机常识的人都知道这些知识。别着急,我们继续。
在Java中对一个文件进行读写操作很简单。假设现在的需求如果是“把D盘的这个文件写入到E盘”,那么我们会提起键盘,啪啪啪啪,搞定!
但其实所谓的文件的“上传(下载)”不是也没什么不同吗?区别就仅仅在于行为由“仅仅在本机之间”转变成了”本机与服务器之间”的文件读写。
这时我们会说,“别逼逼了,这些谁都知道,‘断点续传\'呢?“,其实到了这里也已经很简单了,我们再次明确,断点续传要做的无非就是:
前一次读写行为如果出现中断,请记录下此次读写完成的文件内容的位置信息;当“续传开始”则直接将指针移到此处,开始继续读写操作。
反复的强调原理,实际上是因为只要弄明白了原理,剩下的就只是招式而已了。这就就像武侠小说里的“九九归一”大法一样,最高境界就是回归本源。
任何复杂的事物,只要明白其原理,我们就能将其剥离,还原为一个个简单的事物。同理,一系列简单的事物,经过逻辑组合,就形成了复杂的事物。
下面,我们马上就将回归混沌,以最基本的形式模拟一次“断点续传”。在这里我们连服务器的代码都不去写了,直接通过一个本地测试类搞定。
我们要实现的效果很简单:将在D盘的”test.txt”文件写入到E盘当中,但中途我们会模拟一次”中断”行为,然后在重新继续上传,最终完成整个过程。
也就是说,我们这里将会把“D盘”视作一台电脑,并且直接将”E盘”视作一台服务器。那么这样我们甚至都不再与http协议扯上半毛钱关系了,(当然实际开发我们肯定是还是得与它扯上关系的 ^<^),从而只关心最基本的文件读写的”断”和”续”的原理是怎么样的。
为了通过对比加深理解,我们先来写一段正常的代码,即正常读写,不发生中断:
public class Test { public static void main(String[] args) { // 源文件与目标文件 File sourceFile = new File("D:/", "test.txt"); File targetFile = new File("E:/", "test.txt"); // 输入输出流 FileInputStream fis = null; FileOutputStream fos = null; // 数据缓冲区 byte[] buf = new byte[1]; try { fis = new FileInputStream(sourceFile); fos = new FileOutputStream(targetFile); // 数据读写 while (fis.read(buf) != -1) { System.out.println("write data..."); fos.write(buf); } } catch (FileNotFoundException e) { System.out.println("指定文件不存在"); } catch (IOException e) { // TODO: handle exception } finally { try { // 关闭输入输出流 if (fis != null) fis.close(); if (fos != null) fos.close(); } catch (IOException e) { e.printStackTrace(); } } } }
该段代码运行,我们就会发现在E盘中已经成功拷贝了一份“test.txt”。这段代码很简单,唯一稍微说一下就是:
我们看到我们将buf,即缓冲区 设置的大小是1,这其实就代表我们每次read,是读取一个字节的数据(即1个英文字母)。
现在,我们就来模拟这个读写中断的行为,我们将之前的代码完善如下:
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.RandomAccessFile; public class Test { private static int position = -1; public static void main(String[] args) { // 源文件与目标文件 File sourceFile = new File("D:/", "test.txt"); File targetFile = new File("E:/", "test.txt"); // 输入输出流 FileInputStream fis = null; FileOutputStream fos = null; // 数据缓冲区 byte[] buf = new byte[1]; try { fis = new FileInputStream(sourceFile); fos = new FileOutputStream(targetFile); // 数据读写 while (fis.read(buf) != -1) { fos.write(buf); // 当已经上传了3字节的文件内容时,网络中断了,抛出异常 if (targetFile.length() == 3) { position = 3; throw new FileAccessException(); } } } catch (FileAccessException e) { keepGoing(sourceFile,targetFile, position); } catch (FileNotFoundException e) { System.out.println("指定文件不存在"); } catch (IOException e) { // TODO: handle exception } finally { try { // 关闭输入输出流 if (fis != null) fis.close(); if (fos != null) fos.close(); } catch (IOException e) { e.printStackTrace(); } } } private static void keepGoing(File source,File target, int position) { try { Thread.sleep(10000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { RandomAccessFile readFile = new RandomAccessFile(source, "rw"); RandomAccessFile writeFile = new RandomAccessFile(target, "rw"); readFile.seek(position); writeFile.seek(position); // 数据缓冲区 byte[] buf = new byte[1]; // 数据读写 while (readFile.read(buf) != -1) { writeFile.write(buf); } } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } class FileAccessException extends Exception { }
总结一下,我们在这次改动当中都做了什么工作:
- 首先,我们定义了一个变量position,记录在发生中断的时候,已完成读写的位置。(这是为了方便,实际来说肯定应该讲这个值存到文件或者数据库等进行持久化)
- 然后在文件读写的while循环中,我们去模拟一个中断行为的发生。这里是当targetFile的文件长度为3个字节则模拟抛出一个我们自定义的异常。(我们可以想象为实际下载中,已经上传(下载)了”x”个字节的内容,这个时候网络中断了,那么我们就在网络中断抛出的异常中将”x”记录下来)。
- 剩下的就如果我们之前说的一样,在“续传”行为开始后,通过RandomAccessFile类来包装我们的文件,然后通过seek将指针指定到之前发生中断的位置进行读写就搞定了。
(实际的文件下载上传,我们当然需要将保存的中断值上传给服务器,这个方式通常为httpConnection.setRequestProperty(“RANGE”,”bytes=x”);)
在我们这段代码,开启”续传“行为,即keepGoing方法中:我们起头让线程休眠10秒钟,这正是为了让我们运行程序看到效果。
现在我们运行程序,那么文件就会开启“由D盘上传到E盘的过程”,我们首先点开E盘,会发现的确多了一个test.txt文件,打开它发现内容如下:
没错,这个时候我们发现内容只有“abc”。这是在我们预料以内的,因为我们的程序模拟在文件上传了3个字节的时候发生了中断。
Ok,我们静静的等待10秒钟过去,然后再点开该文件,看看是否能够成功:
通过截图我们发现内容的确已经变成了“abc”,由此也就完成了续传。
摘自:http://blog.csdn.net/ghost_Programmer/article/details/51923895
以上是关于用C实现断点续传的功能,详细点的实现原理是啥嘞的主要内容,如果未能解决你的问题,请参考以下文章