断点续传的原理

来源:互联网 发布:网络音乐版权侵权案例 编辑:程序博客网 时间:2024/05/06 02:46

ftp断点续传的原理 
客户端的实现步骤如下: 

一、下载: 
1、向服务器发送“REST + 本地文件长度”命令,告诉服务器,客户端要断点下载了。这时服务器还不知道客户端要下载哪个文件; 
2、向服务器发送“RETR + 文件名”命令,通知服务器要下载的文件名,这时服务器开始定位文件指针读文件并发送数据。 
3、客户端定位本地文件指针(文件末尾); 
4、两端的准备工作都做完了以后,客户端创建socket,以被动或非被动方式建立数据通道,循环调用recv接收数据并追加入本地文件; 

二、上传: 
1、获取服务器上和本地要上传文件的同名文件大小; 
2、向服务器发送“APPE + 文件名”,通知服务器,接下来从数据通道发送给你的数据要附加到这个文件末尾。 
3、定位本地文件指针(和FTP上文件大小相同的位置) 
4、从文件指针处读数据并发送。 
  代码里将断点上传和断点下载放到同一个函数(MoveFile)里,通过get参数说明是上传还是下载。当然,整个FTP类的实现有800多行,包括登录、退出、获取FTP文件大小、删除FTP服务器上文件、响应服务器,解析响应信息等函数。相应的注释代码里都有,这里就不一一熬述了。 


HTTP断点续传的原理http://www.it.com.cn/f/edu/058/17/159759.htm请求服务器上的一个文时,所发出的请求如下: 
假设服务器域名为wwww.name.org,文件名为name.zip。 
GET /name.zip HTTP/1.1 
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms- 
excel, application/msword, application/vnd.ms-powerpoint, */* 
Accept-Language: zh-cn 
Accept-Encoding: gzip, deflate 
User-Agent: Mozilla/4.0 (compatible; MSIE 5.01; Windows NT 5.0) 
Connection: Keep-Alive 
服务器收到请求后,按要求寻找请求的文件,提取文件的信息,然后返回给浏览器,返回信息如下: 
200 
Content-Length=106786028 
Accept-Ranges=bytes 
Date=Mon, 30 Apr 2001 12:56:11 GMT 
ETag=W/"02ca57e173c11:95b" 
Content-Type=application/octet-stream 
Server=Microsoft-IIS/5.0 
Last-Modified=Mon, 30 Apr 2001 12:56:11 GMT 

所谓断点续传,也就是要从文件已经下载的地方开始继续下载。所以在客户端浏览器传给 
Web服务器的时候要多加一条信息--从哪里开始。 
下面是用自己编的一个"浏览器"来传递请求信息给Web服务器,要求从2000070字节开始。 
GET /gman.zip HTTP/1.0 
User-Agent: NetFox 
RANGE: bytes=2000070- 
Accept: text/html, image/gif, image/jpeg, *; q=.2, */*; q=.2 

仔细看一下就会发现多了一行RANGE: bytes=2000070- 
这一行的意思就是告诉服务器gman.zip这个文件从2000070字节开始传,前面的字节不用传了。 
服务器收到这个请求以后,返回的信息如下: 

206 
Content-Length=106786028 
Content-Range=bytes 2000070-106786027/106786028 
Date=Mon, 30 Apr 2001 12:55:20 GMT 
ETag=W/"02ca57e173c11:95b" 
Content-Type=application/octet-stream 
Server=Microsoft-IIS/5.0 
Last-Modified=Mon, 30 Apr 2001 12:55:20 GMT 

和前面服务器返回的信息比较一下,就会发现增加了一行: 
Content-Range=bytes 2000070-106786027/106786028 

返回的代码也改为206了,而不再是200了。 

http断点续传内核的实现: 

  主要用了6个类,包括一个测试类。 
  SiteFileFetch.java负责整个文件的抓取,控制内部线程(FileSplitterFetch类)。 
  FileSplitterFetch.java负责部分文件的抓取。 
  FileAccess.java负责文件的存储。 
  SiteInfoBean.java要抓取的文件的信息,如文件保存的目录,名字,抓取文件的URL等。 
  Utility.java工具类,放一些简单的方法。 
  TestMethod.java测试类。 

Java代码 
  1. /*  
  2.   **SiteFileFetch.java  
  3.   */   
  4.   package NetFox;   
  5.   import java.io.*;   
  6.   import java.net.*;   
  7.   
  8.   
  9.   public class SiteFileFetch extends Thread {   
  10.   
  11.   
  12.   SiteInfoBean siteInfoBean = null//文件信息Bean   
  13.   long[] nStartPos; //开始位置   
  14.   long[] nEndPos; //结束位置   
  15.   FileSplitterFetch[] fileSplitterFetch; //子线程对象   
  16.   long nFileLength; //文件长度   
  17.   boolean bFirst = true//是否第一次取文件   
  18.   boolean bStop = false//停止标志   
  19.   File tmpFile; //文件下载的临时信息   
  20.   DataOutputStream output; //输出到文件的输出流   
  21.   
  22.   
  23.   public SiteFileFetch(SiteInfoBean bean) throws IOException   
  24.   {   
  25.   siteInfoBean = bean;   
  26.   //tmpFile = File.createTempFile ("zhong","1111",new File(bean.getSFilePath()));   
  27.   tmpFile = new File(bean.getSFilePath()+File.separator + bean.getSFileName()+".info");   
  28.   if(tmpFile.exists ())   
  29.   {   
  30.   bFirst = false;   
  31.   read_nPos();   
  32.   }   
  33.   else   
  34.   {   
  35.   nStartPos = new long[bean.getNSplitter()];   
  36.   nEndPos = new long[bean.getNSplitter()];   
  37.   }   
  38.   
  39.   
  40.   
  41.   }   
  42.   
  43.   
  44.   public void run()   
  45.   {   
  46.   //获得文件长度   
  47.   //分割文件   
  48.   //实例FileSplitterFetch   
  49.   //启动FileSplitterFetch线程   
  50.   //等待子线程返回   
  51.   try{   
  52.   if(bFirst)   
  53.   {   
  54.   nFileLength = getFileSize();   
  55.   if(nFileLength == -1)   
  56.   {   
  57.   System.err.println("File Length is not known!");   
  58.   }   
  59.   else if(nFileLength == -2)   
  60.   {   
  61.   System.err.println("File is not access!");   
  62.   }   
  63.   else   
  64.   {   
  65.   for(int i=0;i<nStartPos.length;i++)   
  66.   {   
  67.   nStartPos[i] = (long)(i*(nFileLength/nStartPos.length));   
  68.   }   
  69.   for(int i=0;i<nEndPos.length-1;i++)   
  70.   {   
  71.   nEndPos[i] = nStartPos[i+1];   
  72.   }   
  73.   nEndPos[nEndPos.length-1] = nFileLength;   
  74.   }   
  75.   }   
  76.   
  77.   
  78.   //启动子线程   
  79.   fileSplitterFetch = new FileSplitterFetch[nStartPos.length];   
  80.   for(int i=0;i<nStartPos.length;i++)   
  81.   {   
  82.   fileSplitterFetch[i] = new FileSplitterFetch(siteInfoBean.getSSiteURL(),   
  83.   siteInfoBean.getSFilePath() + File.separator + siteInfoBean.getSFileName(),   
  84.   nStartPos[i],nEndPos[i],i);   
  85.   Utility.log("Thread " + i + " , nStartPos = " + nStartPos[i] + ", nEndPos = " + nEndPos[i]);   
  86.   fileSplitterFetch[i].start();   
  87.   }   
  88.   // fileSplitterFetch[nPos.length-1] = new FileSplitterFetch(siteInfoBean.getSSiteURL(),   
  89.   siteInfoBean.getSFilePath() + File.separator + siteInfoBean.getSFileName(),nPos[nPos.length-1],nFileLength,nPos.length-1);   
  90.   // Utility.log("Thread " + (nPos.length-1) + " , nStartPos = " + nPos[nPos.length-1] + ",   
  91.   nEndPos = " + nFileLength);   
  92.   // fileSplitterFetch[nPos.length-1].start();   
  93.   
  94.   
  95.   //等待子线程结束   
  96.   //int count = 0;   
  97.   //是否结束while循环   
  98.   boolean breakWhile = false;   
  99.   
  100.   
  101.   while(!bStop)   
  102.   {   
  103.   write_nPos();   
  104.   Utility.sleep(500);   
  105.   breakWhile = true;   
  106.   
  107.   
  108.   for(int i=0;i<nStartPos.length;i++)   
  109.   {   
  110.   if(!fileSplitterFetch[i].bDownOver)   
  111.   {   
  112.   breakWhile = false;   
  113.   break;   
  114.   }   
  115.   }   
  116.   if(breakWhile)   
  117.   break;   
  118.   
  119.   
  120.   //count++;   
  121.   //if(count>4)   
  122.   // siteStop();   
  123.   }   
  124.   
  125.   
  126.   System.err.println("文件下载结束!");   
  127.   }   
  128.   catch(Exception e){e.printStackTrace ();}   
  129.   }   
  130.   
  131. //获得文件长度   
  132.   public long getFileSize()   
  133.   {   
  134.   int nFileLength = -1;   
  135.   try{   
  136.   URL url = new URL(siteInfoBean.getSSiteURL());   
  137.   HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection ();   
  138.   httpConnection.setRequestProperty("User-Agent","NetFox");   
  139.   
  140.   
  141.   int responseCode=httpConnection.getResponseCode();   
  142.   if(responseCode>=400)   
  143.   {   
  144.   processErrorCode(responseCode);   
  145.   return -2//-2 represent access is error   
  146.   }   
  147.   
  148.   
  149.   String sHeader;   
  150.   
  151.   
  152.   for(int i=1;;i++)   
  153.   {   
  154.   //DataInputStream in = new DataInputStream(httpConnection.getInputStream ());   
  155.   //Utility.log(in.readLine());   
  156.   sHeader=httpConnection.getHeaderFieldKey(i);   
  157.   if(sHeader!=null)   
  158.   {   
  159.   if(sHeader.equals("Content-Length"))   
  160.   {   
  161.   nFileLength = Integer.parseInt(httpConnection.getHeaderField(sHeader));   
  162.   break;   
  163.   }   
  164.   }   
  165.   else   
  166.   break;   
  167.   }   
  168.   }   
  169.   catch(IOException e){e.printStackTrace ();}   
  170.   catch(Exception e){e.printStackTrace ();}   
  171.   
  172.   
  173.   Utility.log(nFileLength);   
  174.   
  175.   
  176.   return nFileLength;   
  177.   }   
  178.   
  179.   
  180.   //保存下载信息(文件指针位置)   
  181.   private void write_nPos()   
  182.   {   
  183.   try{   
  184.   output = new DataOutputStream(new FileOutputStream(tmpFile));   
  185.   output.writeInt(nStartPos.length);   
  186.   for(int i=0;i<nStartPos.length;i++)   
  187.   {   
  188.   // output.writeLong(nPos[i]);   
  189.   output.writeLong(fileSplitterFetch[i].nStartPos);   
  190.   output.writeLong(fileSplitterFetch[i].nEndPos);   
  191.   }   
  192.   output.close();   
  193.   }   
  194.   catch(IOException e){e.printStackTrace ();}   
  195.   catch(Exception e){e.printStackTrace ();}   
  196.   }   
  197.   
  198.   
  199.   //读取保存的下载信息(文件指针位置)   
  200.   private void read_nPos()   
  201.   {   
  202.   try{   
  203.   DataInputStream input = new DataInputStream(new FileInputStream(tmpFile));   
  204.   int nCount = input.readInt();   
  205.   nStartPos = new long[nCount];   
  206.   nEndPos = new long[nCount];   
  207.   for(int i=0;i<nStartPos.length;i++)   
  208.   {   
  209.   nStartPos[i] = input.readLong();   
  210.   nEndPos[i] = input.readLong();   
  211.   }   
  212.   input.close();   
  213.   }   
  214.   catch(IOException e){e.printStackTrace ();}   
  215.   catch(Exception e){e.printStackTrace ();}   
  216.   }   
  217.   
  218.   
  219.   private void processErrorCode(int nErrorCode)   
  220.   {   
  221.   System.err.println("Error Code : " + nErrorCode);   
  222.   }   
  223.   
  224.   
  225.   //停止文件下载   
  226.   public void siteStop()   
  227.   {   
  228.   bStop = true;   
  229.   for(int i=0;i<nStartPos.length;i++)   
  230.   fileSplitterFetch[i].splitterStop();   
  231.   
  232.   
  233.   }   
  234.   }   
  235.   /*  
  236.   **FileSplitterFetch.java  
  237.   */   
  238.   package NetFox;   
  239.   
  240.   
  241.   import java.io.*;   
  242.   import java.net.*;   
  243.   
  244.   
  245.   public class FileSplitterFetch extends Thread {   
  246.   
  247.   
  248.   String sURL; //File URL   
  249.   long nStartPos; //File Snippet Start Position   
  250.   long nEndPos; //File Snippet End Position   
  251.   int nThreadID; //Thread's ID   
  252.   boolean bDownOver = false//Downing is over   
  253.   boolean bStop = false//Stop identical   
  254.   FileAccessI fileAccessI = null//File Access interface   
  255.   
  256.   
  257.   public FileSplitterFetch(String sURL,String sName,long nStart,long nEnd,int id) throws IOException   
  258.   {   
  259.   this.sURL = sURL;   
  260.   this.nStartPos = nStart;   
  261.   this.nEndPos = nEnd;   
  262.   nThreadID = id;   
  263.   fileAccessI = new FileAccessI(sName,nStartPos);   
  264.   }   
  265.   
  266.   
  267.   public void run()   
  268.   {   
  269.   while(nStartPos < nEndPos && !bStop)   
  270.   {   
  271.   
  272.   
  273.   try{   
  274.   URL url = new URL(sURL);   
  275.   HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection ();   
  276.   httpConnection.setRequestProperty("User-Agent","NetFox");   
  277.   String sProperty = "bytes="+nStartPos+"-";   
  278.   httpConnection.setRequestProperty("RANGE",sProperty);   
  279.   Utility.log(sProperty);   
  280.   
  281.   
  282.   InputStream input = httpConnection.getInputStream();   
  283.   //logResponseHead(httpConnection);   
  284.   
  285.   
  286.   byte[] b = new byte[1024];   
  287.   int nRead;   
  288.   while((nRead=input.read(b,0,1024)) > 0 && nStartPos < nEndPos && !bStop)   
  289.   {   
  290.   nStartPos += fileAccessI.write(b,0,nRead);   
  291.   //if(nThreadID == 1)   
  292.   // Utility.log("nStartPos = " + nStartPos + ", nEndPos = " + nEndPos);   
  293.   }   
  294.   
  295.   
  296.   Utility.log("Thread " + nThreadID + " is over!");   
  297.   bDownOver = true;   
  298.   //nPos = fileAccessI.write (b,0,nRead);   
  299.   }   
  300.   catch(Exception e){e.printStackTrace ();}   
  301.   }   
  302.   }   
  303. //打印回应的头信息   
  304.   public void logResponseHead(HttpURLConnection con)   
  305.   {   
  306.   for(int i=1;;i++)   
  307.   {   
  308.   String header=con.getHeaderFieldKey(i);   
  309.   if(header!=null)   
  310.   //responseHeaders.put(header,httpConnection.getHeaderField(header));   
  311.   Utility.log(header+" : "+con.getHeaderField(header));   
  312.   else   
  313.   break;   
  314.   }   
  315.   }   
  316.   
  317.   
  318.   public void splitterStop()   
  319.   {   
  320.   bStop = true;   
  321.   }   
  322.   
  323.   
  324.   }   
  325.   
  326.   
  327.   /*  
  328.   **FileAccess.java  
  329.   */   
  330.   package NetFox;   
  331.   import java.io.*;   
  332.   
  333.   
  334.   public class FileAccessI implements Serializable{   
  335.   
  336.   
  337.   RandomAccessFile oSavedFile;   
  338.   long nPos;   
  339.   
  340.   
  341.   public FileAccessI() throws IOException   
  342.   {   
  343.   this("",0);   
  344.   }   
  345.   
  346.   
  347.   public FileAccessI(String sName,long nPos) throws IOException   
  348.   {   
  349.   oSavedFile = new RandomAccessFile(sName,"rw");   
  350.   this.nPos = nPos;   
  351.   oSavedFile.seek(nPos);   
  352.   }   
  353.   
  354.   
  355.   public synchronized int write(byte[] b,int nStart,int nLen)   
  356.   {   
  357.   int n = -1;   
  358.   try{   
  359.   oSavedFile.write(b,nStart,nLen);   
  360.   n = nLen;   
  361.   }   
  362.   catch(IOException e)   
  363.   {   
  364.   e.printStackTrace ();   
  365.   }   
  366.   
  367.   
  368.   return n;   
  369.   }   
  370.   
  371.   
  372.   }   
  373.   
  374.   
  375.   /*  
  376.   **SiteInfoBean.java  
  377.   */   
  378.   package NetFox;   
  379.   
  380.   
  381.   public class SiteInfoBean {   
  382.   
  383.   
  384.   private String sSiteURL; //Site's URL   
  385.   private String sFilePath; //Saved File's Path   
  386.   private String sFileName; //Saved File's Name   
  387.   private int nSplitter; //Count of Splited Downloading File   
  388.   
  389.   
  390.   public SiteInfoBean()   
  391.   {   
  392.   //default value of nSplitter is 5   
  393.   this("","","",5);   
  394.   }   
  395.   
  396.   
  397.   public SiteInfoBean(String sURL,String sPath,String sName,int nSpiltter)   
  398.   {   
  399.   sSiteURL= sURL;   
  400.   sFilePath = sPath;   
  401.   sFileName = sName;   
  402.   this.nSplitter = nSpiltter;   
  403.   
  404.   
  405.   }   
  406.   
  407.   
  408.   public String getSSiteURL()   
  409.   {   
  410.   return sSiteURL;   
  411.   }   
  412.   
  413.   
  414.   public void setSSiteURL(String value)   
  415.   {   
  416.   sSiteURL = value;   
  417.   }   
  418.   
  419.   
  420.   public String getSFilePath()   
  421.   {   
  422.   return sFilePath;   
  423.   }   
  424.   
  425.   
  426.   public void setSFilePath(String value)   
  427.   {   
  428.   sFilePath = value;   
  429.   }   
  430.   
  431.   
  432.   public String getSFileName()   
  433.   {   
  434.   return sFileName;   
  435.   }   
  436.   
  437.   
  438.   public void setSFileName(String value)   
  439.   {   
  440.   sFileName = value;   
  441.   }   
  442.   
  443.   
  444.   public int getNSplitter()   
  445.   {   
  446.   return nSplitter;   
  447.   }   
  448.   
  449.   
  450.   public void setNSplitter(int nCount)   
  451.   {   
  452.   nSplitter = nCount;   
  453.   }   
  454.   }   
  455.   
  456.   
  457.   /*  
  458.   **Utility.java  
  459.   */   
  460.   package NetFox;   
  461.   
  462.   
  463.   public class Utility {   
  464.   
  465.   
  466.   public Utility()   
  467.   {   
  468.   
  469.   
  470.   }   
  471.   
  472.   
  473.   public static void sleep(int nSecond)   
  474.   {   
  475.   try{   
  476.   Thread.sleep(nSecond);   
  477.   }   
  478.   catch(Exception e)   
  479.   {   
  480.   e.printStackTrace ();   
  481.   }   
  482.   }   
  483.   
  484.   
  485.   public static void log(String sMsg)   
  486.   {   
  487.   System.err.println(sMsg);   
  488.   }   
  489.   
  490.   
  491.   public static void log(int sMsg)   
  492.   {   
  493.   System.err.println(sMsg);   
  494.   }   
  495.   }   
  496.   
  497.   
  498.   /*  
  499.   **TestMethod.java  
  500.   */   
  501.   package NetFox;   
  502.   
  503.   
  504.   public class TestMethod {   
  505.   
  506.   
  507.   public TestMethod()   
  508.   { ///xx/weblogic60b2_win.exe   
  509.   try{   
  510.   SiteInfoBean bean = new SiteInfoBean("http://localhost/xx/weblogic60b2_win.exe","L://temp","weblogic60b2_win.exe",5);   
  511.   //SiteInfoBean bean = new SiteInfoBean("http://localhost:8080/down.zip","L://temp","weblogic60b2_win.exe",5);   
  512.   SiteFileFetch fileFetch = new SiteFileFetch(bean);   
  513.   fileFetch.start();   
  514.   }   
  515.   catch(Exception e){e.printStackTrace ();}   
  516.   
  517.   
  518.   }   
  519.   
  520.   
  521.   public static void main(String[] args)   
  522.   {   
  523.   new TestMethod();   
  524.   }   
  525.   }   

原创粉丝点击