多线程断点续传的原理及其简单实现

来源:互联网 发布:人工智能中英文介绍 编辑:程序博客网 时间:2024/04/30 11:59
所谓的断点续传就是指:文件在传输过程式中被中断后,在重新传输时,可以从上次的断点处开始传输,这样就可节省时间,和其它资源。

实现流程:
1.客户端向服务端请求文件信息(名称,长度);
2.客户端跟据文件长度开辟N个线程连接服务端;
3. 服务端开辟新的线程与客户端通信并传输文件;
4.客户端将每线程数据保存到一个文件;
5.合并所有线程文件。


在这里有两个关键点
其一是检测本地已经下载的文件长度和断点值;
其二是在服务端调整文件指针到断点处

实现方法:
我们用一个简单的方法来实现断点续传的功能.在传输文件的时候创建一个临时文件用来存放文件的断点位置
在每次发送接受文件时,先检查有没有临时文件,如果有的话就从临时文件中读取断点值,并把文件指针移动到
断点位置开始传输,这样便可以做到断点续传了。

实现代码:

public class Demo {
   
    privatestatic int ThreadCount = 3;
    privatestatic int runningThread= 3;

    publicstatic void main(String[] args) {
       Demo demo =new Demo();
      demo.Download();
    }
   
   
    private voidDownload(){
       String path= "http://localhost:8080/QQ.exe";
       URLurl;
       try {
          url = newURL(path);
         HttpURLConnection connection = (HttpURLConnection)url.openConnection();
         connection.setConnectTimeout(5000);
         connection.setRequestMethod("GET");
          int code =connection.getResponseCode();
          if (code ==200) {
            //服务器返回的文件长度   其实就是文件的大小
             int length =connection.getContentLength();
            System.out.println("文件的长度:"+length);
            
            //在本地创建一个和服务器一样大小的临时文件
            RandomAccessFile raf = new RandomAccessFile("QQ1.exe","rwd");
            raf.setLength(length);
            
            //假设三个线程下载资源
            //平均每一个线程下载的文件的大小
             intblockSize = length/ThreadCount;
             for (intthreadId = 1; threadId <= ThreadCount; threadId++) {
               //每一个线程下载的开始和结束位置
                intstartIndex = (threadId - 1)*blockSize;
                int endIndex= threadId*blockSize - 1;
               //最后一个线程下载的长度长点
                if (threadId== ThreadCount) {
                   endIndex =length;
                }
               System.out.println("每个线程下载的起止位置:"+threadId+":"+startIndex+"------->"+endIndex);
                newDownloadThread(startIndex, endIndex, threadId, path).start();
             }
          }else{
            System.out.println("服务器错误");
          }
       } catch(Exception e) {
          // TODOAuto-generated catch block
         e.printStackTrace();
       }
      
      
    }
   
    publicstatic class DownloadThread extends Thread{
       private intstartIndex;
       private intendIndex;
       private intthreadId;
       privateString path;
      
      
       publicDownloadThread(int startIndex, int endIndex, int threadId,
             String path){
         super();
         this.startIndex = startIndex;
         this.endIndex = endIndex;
         this.threadId = threadId;
          this.path =path;
       }
      
       public voidrun() {
          try {
            
            //检查是否存在记录下载长度的文件,如果存在,读取这个文件的数据
             Filetempfile = new File(threadId+".txt");
             if(tempfile.exists() && tempfile.length() > 0 ) {
               FileInputStream fis = new FileInputStream(tempfile);
                byte[] temp= new byte[1024];
                int length =fis.read(temp);
               
                StringdownloadLength = new String(temp,0,length);
                intdownloadLengthInt = Integer.parseInt(downloadLength);
               //修改下载的真实的开始位置
               startIndex=downloadLengthInt;
               fis.close();
             }
            
            
             URL url =new URL(path);
            HttpURLConnection connection = (HttpURLConnection)url.openConnection();
            connection.setRequestMethod("GET");
            //请求服务器下载部分的文件   指定文件的大小
            connection.setRequestProperty("Range","bytes="+startIndex+"-"+endIndex);
            System.out.println("线程"+threadId+"真实下载:----"+startIndex+"----------->"+endIndex);
            connection.setReadTimeout(5000);
             int code =connection.getResponseCode();
            System.out.println("code:"+code);
            
             if (code ==206) {
               //返回全部的资源,由于定义了大小,返回的是当前位置文件对应的输入流
                InputStreaminputStream = connection.getInputStream();
               RandomAccessFile raf = new RandomAccessFile("QQ1.exe","rwd");
               raf.seek(startIndex);//定位文件
               
                int len =0;
                byte[] buf =new byte[1024];
                int total =0; //已经下载的文件的长度
                File file =new File(threadId+".txt"); //作用,记录当前线程下载的数据长度
                while ((len= inputStream.read(buf)) != -1){
                  FileOutputStream fos = new FileOutputStream(file);

                  raf.write(buf, 0, len);
                   total +=len;
                  fos.write(String.valueOf(total+startIndex).getBytes());
                  fos.close();
                  
                }
               inputStream.close();
               raf.close();
               
               System.out.println("线程:"+threadId+"下载完毕。。。。。");
             }else{
               System.out.println("文件下载失败了。。。。。");
             }
            
//             FiledeleteFile = new File(threadId+".txt");
//            deleteFile.delete();//当下载完成后清除所有下载记录
            
            
          } catch(IOException e) {
             // TODOAuto-generated catch block
            e.printStackTrace();
         }finally{
            runningThread --;
            if(runningThread == 0){
                for (int i =1; i <= 3; i++) {
                   File file =new File(i+".txt");
                  file.delete();
                }
               System.out.println("所有线程下载完毕,并且清除所有的文件记录");
             }
          }
       }
           
    }

}


0 0