多线程下载原理及核心代码

原文出自:http://blog.csdn.net/howlaa/article/details/21875991


假如我们把一个服务器上的文件看作是一个水缸里的水的话,那么多线程下载就相当于从水缸上打了多个小孔,然后塞进去小管道进行抽水。呵呵,也许这个比喻不够准确。多线程下载大致可分为以下几个步骤:

一、首先在本地创建一个与服务器文件大小相同的临时文件(这个很好理解,如果我想下个2G的电影,我得给先在本地占用2G的空间,不然不能下着下着没空间了是吧)。

二、计算分配几个线程去下载服务器上的资源,知道每个线程下载文件的起始位置。

  那么这个起始位置怎么计算呢?

文件长度/线程个数= 每个线程下载文件的大小。那么

线程1下载的位置:0~每个线程下载文件的大小-1.

线程2:以此类推

那么就是i线程的下载起始位置: (i-1)*每个线程下载文件的大小

三、开启多个线程,每一个线程下载对应位置的文件。

四、如果所有的线程都把自己的数据下载完毕了,服务器上的资源就被下载到本地了。

五、当文件都下载到本地了,那么还有一个文件就是把各个线程下载的文件如何串起来。那么就要利用到一个类:RandomAccessFile 随机文件访问类。

代码如下:

  1. import java.io.InputStream;  
  2. import java.io.RandomAccessFile;  
  3. import java.net.HttpURLConnection;  
  4. import java.net.URL;  
  5.   
  6.   
  7. public class Demo {  
  8.     public static int threadCount = 3;  
  9.     /** 
  10.      * @param args 
  11.      */  
  12.     public static void main(String[] args) throws Exception{  
  13.         //连接服务器,获取文件长度,在本地创建一个大小和服务器一样大的临时文件  
  14.         String path ="http://192.168.1.100:8080/360.exe";  
  15.         URL url = new URL(path);  
  16.         HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
  17.         conn.setConnectTimeout(5000);  
  18.         conn.setRequestMethod("GET");  
  19.         int code = conn.getResponseCode();  
  20.         if(code==200){  
  21.             //服务器返回的数据的长度,实际上就是文件的长度  
  22.             int length = conn.getContentLength();  
  23.             System.out.println("文件总长度:"+length);  
  24.             RandomAccessFile raf = new RandomAccessFile("setup.exe""rwd");  
  25.             //指定创建的文件的长度  
  26.             raf.setLength(length);  
  27.             raf.close();  
  28.             //在客户端本地   
  29.             //假设3个线程去下载资源  
  30.             //平均每一个线程下载的文件的大小。  
  31.             int blockSize = length / threadCount;  
  32.               
  33.             for(int threadId=1;threadId<=threadCount;++threadId){  
  34.                 //第一个线程下载的开始位置  
  35.                 int startIndex = (threadId-1)*blockSize;  
  36.                 int endIndex = blockSize - 1;  
  37.                 if(threadId==threadCount){  
  38.                     //最后一个线程下载的长度稍微长一点  
  39.                     endIndex = length;  
  40.                 }  
  41.                 System.out.println("线程:"+threadId+"下载:--"+startIndex+"-->"+endIndex);  
  42.                 new DownLoadThread(threadId, startIndex, endIndex, path).start();  
  43.             }  
  44.         }else{  
  45.             System.out.println("访问错误");  
  46.         }  
  47.           
  48.     }  
  49.       
  50.     /** 
  51.      * 下载文件的子线程,每个线程下载对应的文件 
  52.      * 
  53.      */  
  54.     public static class DownLoadThread extends Thread{  
  55.         private int threadId;  
  56.         private int startIndex;  
  57.         private int endIndex;  
  58.         private String path;  
  59.         /** 
  60.          * @param threadId线程ID 
  61.          * @param startIndex 
  62.          * @param endIndex 
  63.          * @param path 下载文件在服务器上的路径 
  64.          */  
  65.         public DownLoadThread(int threadId, int startIndex, int endIndex,String path) {  
  66.             this.threadId = threadId;  
  67.             this.startIndex = startIndex;  
  68.             this.endIndex = endIndex;  
  69.             this.path = path;  
  70.         }  
  71.   
  72.         @Override  
  73.         public void run() {  
  74.             try{  
  75.                 URL url = new URL(path);  
  76.                 HttpURLConnection conn = (HttpURLConnection) url.openConnection();  
  77.                 conn.setRequestMethod("GET");  
  78.                 //很重要:请求服务器下载部分的文件的指定的位置:  
  79.                 conn.setRequestProperty("Range""bytes="+startIndex+"-"+endIndex);  
  80.                 conn.setConnectTimeout(5000);  
  81.                 int code  = conn.getResponseCode();//从服务器请求全部资源 200ok ,如果请求部分资源 206 ok  
  82.                 System.out.println("code="+code);  
  83.                   
  84.                 InputStream is = conn.getInputStream();//返回资源  
  85.                 RandomAccessFile raf = new RandomAccessFile("setup.exe""rwd");  
  86.                 //随机写文件的时候从哪个位置开始写  
  87.                 raf.seek(startIndex);//定位文件  
  88.                   
  89.                 int len =0;  
  90.                 byte[] buffer = new byte[1024];  
  91.                 while((len = is.read(buffer)) != -1){  
  92.                     raf.write(buffer,0,len);  
  93.                 }  
  94.                 is.close();  
  95.                 raf.close();  
  96.                 System.out.println("线程"+threadId+"下载完毕");  
  97.                   
  98.             }catch(Exception e){  
  99.                 e.printStackTrace();  
  100.             }  
  101.         }  
  102.           
  103.     }  
  104.   

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值