gaok1 发表于 2013-2-7 10:27:15

(转)断点续传的原理

关键字: java http ftp 断点续传

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测试类。
/*     **SiteFileFetch.java     */      package NetFox;      import java.io.*;      import java.net.*;        public class SiteFileFetch extends Thread {        SiteInfoBean siteInfoBean = null; //文件信息Bean      long[] nStartPos; //开始位置      long[] nEndPos; //结束位置      FileSplitterFetch[] fileSplitterFetch; //子线程对象      long nFileLength; //文件长度      boolean bFirst = true; //是否第一次取文件      boolean bStop = false; //停止标志      File tmpFile; //文件下载的临时信息      DataOutputStream output; //输出到文件的输出流        public SiteFileFetch(SiteInfoBean bean) throws IOException      {      siteInfoBean = bean;      //tmpFile = File.createTempFile ("zhong","1111",new File(bean.getSFilePath()));      tmpFile = new File(bean.getSFilePath()+File.separator + bean.getSFileName()+".info");      if(tmpFile.exists ())      {      bFirst = false;      read_nPos();      }      else      {      nStartPos = new long;      nEndPos = new long;      }            }        public void run()      {      //获得文件长度      //分割文件      //实例FileSplitterFetch      //启动FileSplitterFetch线程      //等待子线程返回      try{      if(bFirst)      {      nFileLength = getFileSize();      if(nFileLength == -1)      {      System.err.println("File Length is not known!");      }      else if(nFileLength == -2)      {      System.err.println("File is not access!");      }      else      {      for(int i=0;i<nStartPos.length;i++)      {      nStartPos = (long)(i*(nFileLength/nStartPos.length));      }      for(int i=0;i<nEndPos.length-1;i++)      {      nEndPos = nStartPos;      }      nEndPos = nFileLength;      }      }        //启动子线程      fileSplitterFetch = new FileSplitterFetch;      for(int i=0;i<nStartPos.length;i++)      {      fileSplitterFetch = new FileSplitterFetch(siteInfoBean.getSSiteURL(),      siteInfoBean.getSFilePath() + File.separator + siteInfoBean.getSFileName(),      nStartPos,nEndPos,i);      Utility.log("Thread " + i + " , nStartPos = " + nStartPos + ", nEndPos = " + nEndPos);      fileSplitterFetch.start();      }      // fileSplitterFetch = new FileSplitterFetch(siteInfoBean.getSSiteURL(),      siteInfoBean.getSFilePath() + File.separator + siteInfoBean.getSFileName(),nPos,nFileLength,nPos.length-1);      // Utility.log("Thread " + (nPos.length-1) + " , nStartPos = " + nPos + ",      nEndPos = " + nFileLength);      // fileSplitterFetch.start();        //等待子线程结束      //int count = 0;      //是否结束while循环      boolean breakWhile = false;        while(!bStop)      {      write_nPos();      Utility.sleep(500);      breakWhile = true;        for(int i=0;i<nStartPos.length;i++)      {      if(!fileSplitterFetch.bDownOver)      {      breakWhile = false;      break;      }      }      if(breakWhile)      break;        //count++;      //if(count>4)      // siteStop();      }        System.err.println("文件下载结束!");      }      catch(Exception e){e.printStackTrace ();}      }      //获得文件长度      public long getFileSize()      {      int nFileLength = -1;      try{      URL url = new URL(siteInfoBean.getSSiteURL());      HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection ();      httpConnection.setRequestProperty("User-Agent","NetFox");        int responseCode=httpConnection.getResponseCode();      if(responseCode>=400)      {      processErrorCode(responseCode);      return -2; //-2 represent access is error      }        String sHeader;        for(int i=1;;i++)      {      //DataInputStream in = new DataInputStream(httpConnection.getInputStream ());      //Utility.log(in.readLine());      sHeader=httpConnection.getHeaderFieldKey(i);      if(sHeader!=null)      {      if(sHeader.equals("Content-Length"))      {      nFileLength = Integer.parseInt(httpConnection.getHeaderField(sHeader));      break;      }      }      else      break;      }      }      catch(IOException e){e.printStackTrace ();}      catch(Exception e){e.printStackTrace ();}        Utility.log(nFileLength);        return nFileLength;      }        //保存下载信息(文件指针位置)      private void write_nPos()      {      try{      output = new DataOutputStream(new FileOutputStream(tmpFile));      output.writeInt(nStartPos.length);      for(int i=0;i<nStartPos.length;i++)      {      // output.writeLong(nPos);      output.writeLong(fileSplitterFetch.nStartPos);      output.writeLong(fileSplitterFetch.nEndPos);      }      output.close();      }      catch(IOException e){e.printStackTrace ();}      catch(Exception e){e.printStackTrace ();}      }        //读取保存的下载信息(文件指针位置)      private void read_nPos()      {      try{      DataInputStream input = new DataInputStream(new FileInputStream(tmpFile));      int nCount = input.readInt();      nStartPos = new long;      nEndPos = new long;      for(int i=0;i<nStartPos.length;i++)      {      nStartPos = input.readLong();      nEndPos = input.readLong();      }      input.close();      }      catch(IOException e){e.printStackTrace ();}      catch(Exception e){e.printStackTrace ();}      }        private void processErrorCode(int nErrorCode)      {      System.err.println("Error Code : " + nErrorCode);      }        //停止文件下载      public void siteStop()      {      bStop = true;      for(int i=0;i<nStartPos.length;i++)      fileSplitterFetch.splitterStop();        }      }      /*     **FileSplitterFetch.java     */      package NetFox;        import java.io.*;      import java.net.*;        public class FileSplitterFetch extends Thread {        String sURL; //File URL      long nStartPos; //File Snippet Start Position      long nEndPos; //File Snippet End Position      int nThreadID; //Thread's ID      boolean bDownOver = false; //Downing is over      boolean bStop = false; //Stop identical      FileAccessI fileAccessI = null; //File Access interface        public FileSplitterFetch(String sURL,String sName,long nStart,long nEnd,int id) throws IOException      {      this.sURL = sURL;      this.nStartPos = nStart;      this.nEndPos = nEnd;      nThreadID = id;      fileAccessI = new FileAccessI(sName,nStartPos);      }        public void run()      {      while(nStartPos < nEndPos && !bStop)      {        try{      URL url = new URL(sURL);      HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection ();      httpConnection.setRequestProperty("User-Agent","NetFox");      String sProperty = "bytes="+nStartPos+"-";      httpConnection.setRequestProperty("RANGE",sProperty);      Utility.log(sProperty);        InputStream input = httpConnection.getInputStream();      //logResponseHead(httpConnection);        byte[] b = new byte;      int nRead;      while((nRead=input.read(b,0,1024)) > 0 && nStartPos < nEndPos && !bStop)      {      nStartPos += fileAccessI.write(b,0,nRead);      //if(nThreadID == 1)      // Utility.log("nStartPos = " + nStartPos + ", nEndPos = " + nEndPos);      }        Utility.log("Thread " + nThreadID + " is over!");      bDownOver = true;      //nPos = fileAccessI.write (b,0,nRead);      }      catch(Exception e){e.printStackTrace ();}      }      }    //打印回应的头信息      public void logResponseHead(HttpURLConnection con)      {      for(int i=1;;i++)      {      String header=con.getHeaderFieldKey(i);      if(header!=null)      //responseHeaders.put(header,httpConnection.getHeaderField(header));      Utility.log(header+" : "+con.getHeaderField(header));      else      break;      }      }        public void splitterStop()      {      bStop = true;      }        }        /*     **FileAccess.java     */      package NetFox;      import java.io.*;        public class FileAccessI implements Serializable{        RandomAccessFile oSavedFile;      long nPos;        public FileAccessI() throws IOException      {      this("",0);      }        public FileAccessI(String sName,long nPos) throws IOException      {      oSavedFile = new RandomAccessFile(sName,"rw");      this.nPos = nPos;      oSavedFile.seek(nPos);      }        public synchronized int write(byte[] b,int nStart,int nLen)      {      int n = -1;      try{      oSavedFile.write(b,nStart,nLen);      n = nLen;      }      catch(IOException e)      {      e.printStackTrace ();      }        return n;      }        }        /*     **SiteInfoBean.java     */      package NetFox;        public class SiteInfoBean {        private String sSiteURL; //Site's URL      private String sFilePath; //Saved File's Path      private String sFileName; //Saved File's Name      private int nSplitter; //Count of Splited Downloading File        public SiteInfoBean()      {      //default value of nSplitter is 5      this("","","",5);      }        public SiteInfoBean(String sURL,String sPath,String sName,int nSpiltter)      {      sSiteURL= sURL;      sFilePath = sPath;      sFileName = sName;      this.nSplitter = nSpiltter;        }        public String getSSiteURL()      {      return sSiteURL;      }        public void setSSiteURL(String value)      {      sSiteURL = value;      }        public String getSFilePath()      {      return sFilePath;      }        public void setSFilePath(String value)      {      sFilePath = value;      }        public String getSFileName()      {      return sFileName;      }        public void setSFileName(String value)      {      sFileName = value;      }        public int getNSplitter()      {      return nSplitter;      }        public void setNSplitter(int nCount)      {      nSplitter = nCount;      }      }        /*     **Utility.java     */      package NetFox;        public class Utility {        public Utility()      {        }        public static void sleep(int nSecond)      {      try{      Thread.sleep(nSecond);      }      catch(Exception e)      {      e.printStackTrace ();      }      }        public static void log(String sMsg)      {      System.err.println(sMsg);      }        public static void log(int sMsg)      {      System.err.println(sMsg);      }      }        /*     **TestMethod.java     */      package NetFox;        public class TestMethod {        public TestMethod()      { ///xx/weblogic60b2_win.exe      try{      SiteInfoBean bean = new SiteInfoBean("http://localhost/xx/weblogic60b2_win.exe","L:\\temp","weblogic60b2_win.exe",5);      //SiteInfoBean bean = new SiteInfoBean("http://localhost:8080/down.zip","L:\\temp","weblogic60b2_win.exe",5);      SiteFileFetch fileFetch = new SiteFileFetch(bean);      fileFetch.start();      }      catch(Exception e){e.printStackTrace ();}        }        public static void main(String[] args)      {      new TestMethod();      }      }
页: [1]
查看完整版本: (转)断点续传的原理