a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 212|回复: 3

[基础知识] java的http断点续传原理(1)

[复制链接]
发表于 2012-8-4 12:37:27 | 显示全部楼层 |阅读模式
(一)断点续传的原理   其实断点续传的原理很简单,就是在Http的请求上和一般的下载有所不同而已。7 z1 P, T$ S, b
  打个比方,浏览器请求服务器上的一个文时,所发出的请求如下:
, c6 p: ^3 ]9 _  假设服务器域名为wwww.sjtu.edu.cn,文件名为down.zip。
' N5 F; Y+ s8 V( u9 J" O1 F  GET /down.zip HTTP/1.12 _$ f/ z/ N. j6 z
  Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-9 s" @$ b- i& P2 z
  excel, application/msword, application/vnd.ms-powerpoint, */*
" `8 K" a+ v1 ^" f" b' `7 ^1 Q; J  Accept-Language: zh-cn- `" e2 Q: I: Q; E8 I) r* V; v- w9 c8 U
  Accept-Encoding: gzip, deflate
( d: k  y: J) v  N6 v  User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)
# E: S5 y' v0 M  Connection: Keep-Alive
! P! u& f0 u8 H6 r. n  服务器收到请求后,按要求寻找请求的文件,提取文件的信息,然后返回给浏览器,返回信息如下:6 F; E" E" F+ ]: y  g, l
  200# d  T) ?' z+ z! N' U2 g1 q
  Content-Length=106786028$ M# R& B0 D) O
  Accept-Ranges=bytes
3 p- X. D- w/ j# n( y3 h" T# Z  Date=Mon, 30 Apr 2001 12:56:11 GMT6 z% `5 g2 k7 X6 i7 B9 |
  ETag=W/"02ca57e173c11:95b"
) b7 ~6 |& G: E: e) l1 ^9 Z5 x8 B  Content-Type=application/octet-stream
& V6 v0 y: u* ]5 a5 d! e  f  Server=Microsoft-IIS/5.0  c; W' x; A; i# H
  Last-Modified=Mon, 30 Apr 2001 12:56:11 GMT
3 R; r$ Q5 q- C2 W; `% A! Z* b  所谓断点续传,也就是要从文件已经下载的地方开始继续下载。所以在客户端浏览器传给
0 e/ ^! m) P; E  Web服务器的时候要多加一条信息--从哪里开始。
7 F# J1 L, h  ?* c  下面是用自己编的一个"浏览器"来传递请求信息给Web服务器,要求从2000070字节开始。
! ^- v5 O8 G1 a& W  GET /down.zip HTTP/1.0$ B6 {; d7 X7 b- B3 q
  User-Agent: NetFox
* O7 e8 R3 I& r  RANGE: bytes=2000070-; Z. ^, \6 n6 ^3 y' U
  Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
+ }( @9 _8 Q7 [. O/ G) U1 z  仔细看一下就会发现多了一行RANGE: bytes=2000070-5 p: F3 r$ s! Z" A: N
  这一行的意思就是告诉服务器down.zip这个文件从2000070字节开始传,前面的字节不用传了。
$ S9 i: c, k7 ~  p& q9 X  服务器收到这个请求以后,返回的信息如下:
0 w4 h8 l( z/ R3 G# o  206: \; z$ a) @4 p+ O, V
  Content-Length=106786028
* C1 {2 Z4 ?4 v  j$ V# R; b  Content-Range=bytes 2000070-106786027/106786028
. `5 {' u! G- j  q# k  Date=Mon, 30 Apr 2001 12:55:20 GMT$ _$ \% K9 O6 W( h, i$ I
  ETag=W/"02ca57e173c11:95b"- N$ k4 M) X5 `/ _: i/ ?, N" b
  Content-Type=application/octet-stream5 Y1 W& E: V2 H; v/ M. r
  Server=Microsoft-IIS/5.02 |: [, F7 N- U8 }* |2 u3 i
  Last-Modified=Mon, 30 Apr 2001 12:55:20 GMT
) {5 O2 ~3 h/ g  和前面服务器返回的信息比较一下,就会发现增加了一行:3 F" N2 l. x( c9 l" J. e! e
  Content-Range=bytes 2000070-106786027/106786028
- n$ m# q" u4 L5 p" s+ g: n# F  返回的代码也改为206了,而不再是200了。
& w6 K0 H: }, ?* s( m2 X/ l- Q6 H  知道了以上原理,就可以进行断点续传的编程了。
2 e/ d2 |" Q, E0 S# x! ~% ~3 y% ~" f6 Z
  
回复

使用道具 举报

 楼主| 发表于 2012-8-4 12:37:28 | 显示全部楼层

java的http断点续传原理(1)

(二)Java实现断点续传的关键几点</p>  (1)用什么方法实现提交RANGE: bytes=2000070-。  q/ v4 m$ ]+ E1 I
  当然用最原始的Socket是肯定能完成的,不过那样太费事了,其实Java的net包中提供了这种功能。代码如下:, X0 G! L  d% E' B% m0 T
  URL url = new URL("http://www.sjtu.edu.cn/down.zip");
/ h$ G  @. B* y  B5 W6 n% {  HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection
* B" L8 R9 D3 `( Q  ();6 Q0 ~' M/ s% C2 X& T
  //设置User-Agent
" v0 c# G5 `% ]2 j2 H4 k  httpConnection.setRequestProperty("User-Agent","NetFox");1 A' q* T  [( z# I* T! `
  //设置断点续传的开始位置
% B( M, H1 ]# C' F1 K: f7 R  httpConnection.setRequestProperty("RANGE","bytes=2000070");
' h5 m: c6 x. `4 D  //获得输入流
$ X5 \' g( M7 l6 B1 f+ \( q5 Q  InputStream input = httpConnection.getInputStream();
1 A4 I8 T: ^" h; ]8 I) `9 L  从输入流中取出的字节流就是down.zip文件从2000070开始的字节流。
" u6 h: l5 }6 |- w8 G( Y( q' C  大家看,其实断点续传用Java实现起来还是很简单的吧。  E! b: W# p0 q) z3 x
  接下来要做的事就是怎么保存获得的流到文件中去了。
3 f+ g( }; o! e0 k: b9 P. f  保存文件采用的方法。" w2 I# e! r3 C( R! h; p, W0 _
  我采用的是IO包中的RandAccessFile类。- L. B+ H8 G8 o# ?' Y  ~; |/ I7 [( c
  操作相当简单,假设从2000070处开始保存文件,代码如下:% L! w* @. N- k  {6 ?
  RandomAccess oSavedFile = new RandomAccessFile("down.zip","rw");
  T* c) v& _% b  long nPos = 2000070;
" l* M; a6 i1 V; Z' v  //定位文件指针到nPos位置
0 r  s. I0 b9 C7 _: f: I5 s  J  oSavedFile.seek(nPos);! T& W% G" {$ ]# b* f
  byte[] b = new byte[1024];6 r/ a/ Y  ^) _  a+ V3 s& L8 Q  E: P! S8 j
  int nRead;
# j, o0 ~, k% }! e  //从输入流中读入字节流,然后写到文件中/ H! x- J) q* m! M  `( g& k3 H
  while((nRead=input.read(b,0,1024)) > 0)6 b: {6 m: w  ]
  {) W5 s6 ~9 l3 w  d7 \( V, Z  _  Q- u
  oSavedFile.write(b,0,nRead);
& p4 a4 S8 R/ ^  }
- n" I# ~7 \  r/ S- M' n  r  怎么样,也很简单吧。6 F0 A) o: J- N" G, _8 Z% {
  接下来要做的就是整合成一个完整的程序了。包括一系列的线程控制等等。
# p/ i& N' Q! i  f; K  d  (三)断点续传内核的实现
6 `' Q1 o4 C# g- P, h3 [2 c1 }* Y  主要用了6个类,包括一个测试类。
5 @& C/ h$ b4 p6 I; d6 p+ W: B  SiteFileFetch.java负责整个文件的抓取,控制内部线程(FileSplitterFetch类)。3 c$ C- C4 E+ U! N4 i% `% J
  FileSplitterFetch.java负责部分文件的抓取。* c, |4 W, U. `
  FileAccess.java负责文件的存储。
) t9 O4 q% l1 }9 G; Z  SiteInfoBean.java要抓取的文件的信息,如文件保存的目录,名字,抓取文件的URL等。; x/ |) I' O4 _. Q$ {) O
  Utility.java工具类,放一些简单的方法。% x' |/ q3 q$ ~) u$ U
  TestMethod.java测试类。  T, f2 u" R+ U% q6 _
  下面是源程序:
+ _* [, G! w+ s  /*
3 Z$ g4 _; l* J: C  **SiteFileFetch.java
: i1 @# _4 R5 u% [+ K  */
% L% G3 D3 `' D6 b* [  package NetFox;
0 v2 N" I% A" s7 P- b. a  import java.io.*;
! E0 q% L5 H4 F# _; q( D% i! t  \  import java.net.*;8 f4 I! R6 ]( y% t
  public class SiteFileFetch extends Thread {) }2 L+ A2 [/ v
  SiteInfoBean siteInfoBean = null; //文件信息Bean
3 q- l/ P, Q3 p9 ~2 c: i; K0 g  long[] nStartPos; //开始位置
3 j- q2 t- j& a  long[] nEndPos; //结束位置
- I0 O2 }3 z" z+ v) w  FileSplitterFetch[] fileSplitterFetch; //子线程对象
) g! i* T9 ~/ K% @+ t  long nFileLength; //文件长度6 w$ }: G6 t& s: O; T
  boolean bFirst = true; //是否第一次取文件
/ p  ~" ^, Q: v! U" [  c+ v
9 X) l, J* s; G4 M, c/ p6 K* n% P2 c  boolean bStop = false; //停止标志
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-4 12:37:29 | 显示全部楼层

java的http断点续传原理(1)

</p>File tmpFile; //文件下载的临时信息2 O1 F+ C' R5 S" N0 `$ a
  DataOutputStream output; //输出到文件的输出流
8 L: e* |5 L& M0 W3 G2 R  public SiteFileFetch(SiteInfoBean bean) throws IOException- G. ~& M  [- j1 r3 z
  {
6 i- [2 F0 F  B; l' ?  siteInfoBean = bean;7 ^. q( S4 o4 v: z! i8 A4 w7 f7 v5 ]& e
  //tmpFile = File.createTempFile ("zhong","1111",new File(bean.getSFilePath()));9 f# s1 W' J1 i3 w
  tmpFile = new File(bean.getSFilePath()+File.separator + bean.getSFileName()+".info");6 |' p: s0 b7 |0 T6 Q3 z4 s9 P
  if(tmpFile.exists ())- @8 P* O8 o% W! x
  {& i6 p  `# K& B" [/ s
  bFirst = false;; R9 O& ?$ B( P4 Q& Q8 {# @
  read_nPos();
, m3 `/ ?# x7 @4 r. g  }% M0 J; e) R/ k5 W+ u
  else, k6 p# s0 l- P7 M5 e# q
  {
. e3 c% `% M! ?# P' M+ R0 r2 u  nStartPos = new long[bean.getNSplitter()];
5 m) g7 z. i0 t& J  nEndPos = new long[bean.getNSplitter()];
% b' J# J+ y2 N" M  }+ u. v# i/ k* G
  }
7 n1 Q/ F2 b- _; O3 P7 g% Q! Z  public void run()4 h; L1 \; C( Z/ u3 Y
  {
5 L& U7 t8 a6 M8 v- c! t) V  //获得文件长度
7 O+ P" I$ \1 a: y4 }+ s( ~9 A  //分割文件# U, p& U( p9 i) f" W# _9 i
  //实例FileSplitterFetch# j5 i% V3 _- I4 W5 X
  //启动FileSplitterFetch线程, w- V  q! J# h8 L+ ^( o0 J4 F
  //等待子线程返回
+ z/ `: z5 }0 S3 ]/ x- D  try{4 g  r# L1 i' p6 Z* C% Z0 d
  if(bFirst)* j# d5 e* `" H  S
  {
* V5 m. L9 P% K2 I2 X  nFileLength = getFileSize();- D% V7 f& d0 ^- O( ^* k
  if(nFileLength == -1)
  F  [6 i$ X0 R# z9 ?9 U  {
* l+ r- G$ k# v4 M  System.err.println("File Length is not known!");) q3 G# _+ B  h! X: p" Z' Q" R8 S
  }
1 C( K  g' N! o' H; C2 S- L  else if(nFileLength == -2)4 D- ~- }9 |* v! i# G
  {
( L9 \4 C9 a; L( E7 t) N+ G" Y) K) ^  System.err.println("File is not access!");
! k1 t# B4 Y  y1 S  }
; o$ G$ m& E* W: C; ~. E  else
' ]/ T- v1 V7 T2 X4 F  {- Z- G1 u/ L4 {6 ?4 j, G7 c

: m5 p2 |. P1 E, e" x& \' B  for(int i=0;i
回复 支持 反对

使用道具 举报

 楼主| 发表于 2012-8-4 12:37:30 | 显示全部楼层

java的http断点续传原理(1)

</p>  {
' j5 E; _3 D, h; ^( V) N8 e- X  nEndPos = nStartPos[i+1];
( A- c8 a+ N7 L, F* A# r  }
# H& h! y. L% Y# K, B$ S/ ~  nEndPos[nEndPos.length-1] = nFileLength;9 o& W3 `/ ^8 k2 h7 j" v
  }# m3 C2 A8 A- L: v* r+ M: k
  }# f0 E+ a, N# ?
  //启动子线程; N) p2 {9 _9 }7 k: A; Q
  fileSplitterFetch = new FileSplitterFetch[nStartPos.length];
6 `1 A1 q6 m* f* @6 p
/ R9 t+ G: M% f+ k  for(int i=0;i
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|Woexam.Com ( 湘ICP备18023104号 )

GMT+8, 2024-4-29 22:25 , Processed in 0.331134 second(s), 27 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表