a我考网

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

查看: 407|回复: 5

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

[复制链接]
发表于 2012-8-4 12:37:27 | 显示全部楼层 |阅读模式
  (一)断点续传的原理) @2 d2 b; F1 {9 Y+ K' Q
  其实断点续传的原理很简单,就是在Http的请求上和一般的下载有所不同而已。
4 J8 x: [6 ?5 V) w2 w5 i; ?5 _' W  打个比方,浏览器请求服务器上的一个文时,所发出的请求如下:
0 j) r7 R3 C* I" u$ m  假设服务器域名为wwww.sjtu.edu.cn,文件名为down.zip。
. o, j6 s: }$ M  GET /down.zip HTTP/1.18 N" i9 ?5 b2 [2 w
  Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-
% i  Z# ~) R: @5 }8 Q2 ]% H1 ]  excel, application/msword, application/vnd.ms-powerpoint, */*
* S4 ]0 |( C+ d1 A7 Q  Accept-Language: zh-cn
' q- G  j" A# v* a3 o/ R4 [' \  Accept-Encoding: gzip, deflate) ?. I0 M5 y& {# e
  User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0)( D2 N0 K0 Q- M3 C7 I; [
  Connection: Keep-Alive
' W! Q3 q  e0 v/ ~4 v2 k  服务器收到请求后,按要求寻找请求的文件,提取文件的信息,然后返回给浏览器,返回信息如下:) C. t/ _3 p" b+ }
  200
. o! [$ t4 u3 g# |- v$ `- r  Content-Length=1067860285 _  D2 H$ |% Z
  Accept-Ranges=bytes
1 W, E  _# Q3 O" n  Date=Mon, 30 Apr 2001 12:56:11 GMT
$ u0 ]0 A9 D) ?1 W1 Q/ u  ETag=W/"02ca57e173c11:95b"
! a' ^+ H1 j& V  b" }% G  Content-Type=application/octet-stream
$ P8 x, u, E' V  Server=Microsoft-IIS/5.0
1 f: x) ]1 \1 t8 i6 i7 N! T: x5 D# ^  Last-Modified=Mon, 30 Apr 2001 12:56:11 GMT
4 }; A5 p' d" e$ `9 _' P' O  所谓断点续传,也就是要从文件已经下载的地方开始继续下载。所以在客户端浏览器传给6 V4 ^5 ]& v; a

7 t, a; A" I9 J; O1 G  Web服务器的时候要多加一条信息--从哪里开始。
回复

使用道具 举报

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

JAVA基础:java的http断点续传原理(一)

</p>  下面是用自己编的一个"浏览器"来传递请求信息给Web服务器,要求从2000070字节开始。
8 _* p& Z; @0 H* `- G/ a) o  GET /down.zip HTTP/1.06 }3 I% B( N7 Z/ B9 }% q  `2 E
  User-Agent: NetFox
) d: p) X0 H4 I3 ~1 ^  {  RANGE: bytes=2000070-( @, N8 Y) O: G
  Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2
9 F+ ~& ?2 z$ A' Z/ p) G' j  仔细看一下就会发现多了一行RANGE: bytes=2000070-
( L3 A8 J1 ^5 z9 w  这一行的意思就是告诉服务器down.zip这个文件从2000070字节开始传,前面的字节不用传了。/ J  D5 }" W: S5 s
  服务器收到这个请求以后,返回的信息如下:5 g( T5 W' j  _' e+ Q9 l
  206
1 D% v+ O4 z, Y2 J0 r/ D6 m3 Q' L  Content-Length=106786028
* E/ x" Q$ P' y) ~) x/ Y) [# |  Content-Range=bytes 2000070-106786027/106786028- d* u1 X5 o5 q
  Date=Mon, 30 Apr 2001 12:55:20 GMT
' r3 n4 y/ w' o2 a9 I! V6 ]2 d  ETag=W/"02ca57e173c11:95b"
5 w. ], h$ \6 e; k5 M( P1 E  Content-Type=application/octet-stream
( w- E" B& W: Q  Server=Microsoft-IIS/5.0
* E1 S" d6 O' E0 a! N% x( p+ T" x  Last-Modified=Mon, 30 Apr 2001 12:55:20 GMT, ?" m* F; {$ {( w  W# e: L
  和前面服务器返回的信息比较一下,就会发现增加了一行:4 K1 u# T% c, m% ^  c4 i2 j( j$ \
  Content-Range=bytes 2000070-106786027/106786028
5 H  W8 H2 k& H' ?; [5 l6 R1 Y  返回的代码也改为206了,而不再是200了。
8 Z  X* h$ m. y" i5 B  知道了以上原理,就可以进行断点续传的编程了。
# `" ~4 F% J' Y2 A  (二)Java实现断点续传的关键几点9 r: Q3 x3 ^% e
  (1)用什么方法实现提交RANGE: bytes=2000070-。
, Y! G/ p& Z2 G+ A' k  当然用最原始的Socket是肯定能完成的,不过那样太费事了,其实Java的net包中提供了这种功能。代码如下:$ Z/ o$ A" Y! f0 ^0 C3 h% _. \6 E
  URL url = new URL("http://www.sjtu.edu.cn/down.zip");
) ~! l# @" R3 I0 H2 R+ Y  HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection2 k1 W" [  _9 y: G4 A4 t
  ();0 Z/ }- M% m7 Y; G
  //设置User-Agent
- f& H" @9 g7 \7 W$ ^  httpConnection.setRequestProperty("User-Agent","NetFox");
6 j- C8 t, a1 N; L/ s- Y  //设置断点续传的开始位置
# |; v' u/ X2 Q( g& |5 `  httpConnection.setRequestProperty("RANGE","bytes=2000070");# W0 W4 a+ F* k9 g; A
  //获得输入流
4 x$ n5 ^4 i9 M8 e# H2 E  InputStream input = httpConnection.getInputStream();  S# ]7 ]3 p- Z( _4 {7 H, X
  从输入流中取出的字节流就是down.zip文件从2000070开始的字节流。
7 `4 f! w. h4 Y* P2 K9 Q  大家看,其实断点续传用Java实现起来还是很简单的吧。
- ?: g9 ?) [+ R0 [
4 I: D( `$ z: J. |) P* {  接下来要做的事就是怎么保存获得的流到文件中去了。
回复 支持 反对

使用道具 举报

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

JAVA基础:java的http断点续传原理(一)

</p>  保存文件采用的方法。
' R1 W5 H1 d& B$ x1 K, H1 W  我采用的是IO包中的RandAccessFile类。
* ^5 J8 L7 y, u) G, e+ m9 E( D4 |! y  \  操作相当简单,假设从2000070处开始保存文件,代码如下:' @8 r( L; ?" l$ d& ?$ @" E
  RandomAccess oSavedFile = new RandomAccessFile("down.zip","rw");: `- P* {9 `( f
  long nPos = 2000070;
4 }+ ~0 z4 h% ?- S6 G$ \  //定位文件指针到nPos位置
  {5 {1 ^8 a. {4 g  oSavedFile.seek(nPos);' |( Y7 _& t1 d$ [0 h. p/ ?% h
  byte[] b = new byte[1024];
4 _" j$ n+ j; X5 G9 _0 t% E  int nRead;
! n# t: I3 M; J  //从输入流中读入字节流,然后写到文件中
; x8 r1 n$ v7 P( n  while((nRead=input.read(b,0,1024)) > 0)
4 H7 i, `- N5 x; o8 v3 a1 ~; ?  {9 \5 l% l: I* R. C
  oSavedFile.write(b,0,nRead);
) z5 ~. g) e5 o2 n+ x  }% g2 Z- U6 `9 F) h$ e
  怎么样,也很简单吧。! o" u3 w2 `- K. r9 ?
  接下来要做的就是整合成一个完整的程序了。包括一系列的线程控制等等。
! ]. w" ], Q" O* N2 l  (三)断点续传内核的实现
8 l$ {4 z- M! p6 ?9 S  主要用了6个类,包括一个测试类。
0 J9 Q2 P) N* [2 R# j  SiteFileFetch.java负责整个文件的抓取,控制内部线程(FileSplitterFetch类)。, ?" {8 r' K+ C$ T$ {+ `- M
  FileSplitterFetch.java负责部分文件的抓取。
& s; L  e% m: W8 N2 m  FileAccess.java负责文件的存储。! H; S4 a& v0 K* y8 S
  SiteInfoBean.java要抓取的文件的信息,如文件保存的目录,名字,抓取文件的URL等。
9 L' |" x  }' D8 F: z/ u  Utility.java工具类,放一些简单的方法。
& c  l8 g+ r- t+ O1 }  TestMethod.java测试类。/ m2 n6 g9 C0 n! x1 C5 G+ O4 Q
  下面是源程序:
% r/ N" L6 J2 A2 T" w2 Z  /*
4 T- K, z, k8 k  **SiteFileFetch.java
- n8 U7 j3 S( E1 I* {  */$ c6 F# R9 S; u  K9 Q: r0 b% J9 j
  package NetFox;
0 M( a$ ?7 Z3 f) J  import java.io.*;7 \- k$ \8 G1 o; Q( R3 D0 t. }, l
  import java.net.*;( [( E8 I* }$ P1 ]# B+ z
  public class SiteFileFetch extends Thread {0 }& n" I1 }2 o: I
  SiteInfoBean siteInfoBean = null; //文件信息Bean( d# u- x7 x8 @2 K6 v/ i. U
  long[] nStartPos; //开始位置+ R  G. ^. z5 ]2 o. ]
  long[] nEndPos; //结束位置' v$ {- j0 h/ l' T3 }" A6 u
  FileSplitterFetch[] fileSplitterFetch; //子线程对象
  d- l3 H7 c4 c4 a0 o- H* n0 j  long nFileLength; //文件长度
7 ~, z5 ]! J% M" V" M7 a  boolean bFirst = true; //是否第一次取文件
9 ~3 d( ^, m' I, u( e, T9 p  boolean bStop = false; //停止标志
回复 支持 反对

使用道具 举报

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

JAVA基础:java的http断点续传原理(一)

File tmpFile; //文件下载的临时信息   DataOutputStream output; //输出到文件的输出流
6 Q$ ^9 p; {5 X4 D  public SiteFileFetch(SiteInfoBean bean) throws IOException  ~6 d; w8 _) p+ A* u  ^
  {
& z  I. L: a, `7 q" r: A  siteInfoBean = bean;
3 y+ `; y+ ]- f1 a( _  //tmpFile = File.createTempFile ("zhong","1111",new File(bean.getSFilePath()));2 X+ Q6 l9 H; S/ F) x
  tmpFile = new File(bean.getSFilePath()+File.separator + bean.getSFileName()+".info");. w- X$ J# S1 E0 h- ~
  if(tmpFile.exists ())4 D5 i. e1 d: o
  {! u& q+ ~1 c4 Z, E5 z- I
  bFirst = false;4 ?% a- i( i9 C) s0 H
  read_nPos();. ^8 ^: t0 R, [* R* s* P6 U
  }4 b) N% |* m! U5 N2 U- Y
  else
  O' z! R/ c" A7 r7 d  {; \! D, @3 E& b' S
  nStartPos = new long[bean.getNSplitter()];3 B7 R! |# T3 p& D& {9 s# _
  nEndPos = new long[bean.getNSplitter()];
* R1 [/ z0 O% e5 y6 V0 t  }( V% W9 m! `+ N: L# M4 t' `1 [+ s& g
  }
1 I9 d. l) [; P+ n4 Y  public void run()
2 z( `* C/ n" a) ]0 V0 q& ^
- a& F+ k  E2 l  {
回复 支持 反对

使用道具 举报

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

JAVA基础:java的http断点续传原理(一)

</p>  //获得文件长度+ `/ E* O  l2 o1 g4 m* B- ^
  //分割文件9 P! [8 B$ d" p3 ~
  //实例FileSplitterFetch
5 ?! j9 i5 l5 Y5 t6 e+ F  //启动FileSplitterFetch线程( t( K. R6 l( Q" K
  //等待子线程返回1 R* ]3 O" j& D- X. ?( `8 d+ a# `5 ~
  try{- x& s3 j" o, x* f$ Q( u
  if(bFirst)
. z+ C7 m8 J$ C5 K' i% X  {
# \" x  H0 z& L# F8 c) s! `- I  nFileLength = getFileSize();
$ R9 B7 T9 D& z  if(nFileLength == -1)
; N: q% f5 t, S1 H# C+ S4 d  {
4 d8 z5 ^; \1 q  System.err.println("File Length is not known!");
( z" }$ A7 k: ]) N: c1 {# x- p, c  }
' P" o& k7 a$ F0 i  else if(nFileLength == -2)
, i- v( o: D7 q7 H- A. e  {9 X1 s9 B" [" |! e7 v
  System.err.println("File is not access!");
8 Y2 ~/ c* v- Q* `# R  }! l6 f$ z% S; m
  else! A2 P& f+ {' G: W7 @6 e
  {! k' q" o$ b! V5 b6 w1 {6 G5 J
! q9 a# D6 Z7 A) ~2 L4 y" H9 G5 ^
  for(int i=0;i
回复 支持 反对

使用道具 举报

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

JAVA基础:java的http断点续传原理(一)

</p>  // fileSplitterFetch[nPos.length-1] = new FileSplitterFetch(siteInfoBean.getSSiteURL(),
( Q& ^7 ~: |) h5 x& e  siteInfoBean.getSFilePath() + File.separator + siteInfoBean.getSFileName(),nPos[nPos.length-1],nFileLength,nPos.length-1);# F: C1 y( f' ^9 _% \3 u# s
  // Utility.log("Thread " + (nPos.length-1) + " , nStartPos = " + nPos[nPos.length-1] + ",
/ T% f4 U# A- V: Z  M: P  nEndPos = " + nFileLength);: Y! r- I2 o+ ?! C% k3 Y
  // fileSplitterFetch[nPos.length-1].start();3 m+ a) A6 j, t  M* ]# n
  //等待子线程结束4 ~+ l% P* x0 k& W$ H) C
  //int count = 0;& R# M# u% ?; S# U1 T! N
  //是否结束while循环
; `" j8 J7 A" K3 V/ B6 m  boolean breakWhile = false;
- Z: j# z/ f# w) C  while(!bStop)
( W1 B% b9 J* J; L& i* x  {, o4 t5 b* L. Z0 L
  write_nPos();
( `/ x; T. {, a7 Y9 X" N+ e  Q  Utility.sleep(500);# G  W+ k6 S7 H- d
  breakWhile = true;
: w. b0 `2 J0 \  for(int i=0;i4)
7 x2 g, W. R; z& y  // siteStop();6 Z3 c1 v# u6 Y% a% ^) r
  }
3 U  J2 v* g2 m, A+ P1 u  System.err.println("文件下载结束!");9 u5 V* @9 a4 p2 o' P
  }7 N* D3 O* a5 Z1 u0 o
  catch(Exception e){e.printStackTrace ();}
/ Y5 q% K& W% C8 Q) |$ b, @  }
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2024-4-29 20:38 , Processed in 0.291869 second(s), 31 queries .

Powered by Discuz! X3.4 Licensed

© 2001-2017 Comsenz Inc.

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