1.
结果:
133169
线程0开始下载
线程1开始下载
线程2开始下载
线程_1的下载起点是 44389 下载终点是: 88777
线程_0的下载起点是 0 下载终点是: 44388
线程_2的下载起点是 88778 下载终点是: 133168
线程2下载完毕
线程1下载完毕
线程0下载完毕
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 package ck.cn; 2 3 import java.io.File; 4 import java.io.InputStream; 5 import java.io.RandomAccessFile; 6 import java.net.HttpURLConnection; 7 import java.net.URL; 8 9 /** 10 * 多线程下载 11 * 12 * 13 */ 14 public class MultiThreadDown { 15 private String path = ""; 16 private String targetFilePath=""; //下载文件存放目录 17 private int threadCount = 3; //线程数量 18 19 /** 20 * 构造方法 21 * @param path 要下载文件的网络路径 22 * @param targetFilePath 保存下载文件的目录 23 * @param threadCount 开启的线程数量,默认为 3 24 */ 25 public MultiThreadDown(String path, String targetFilePath, int threadCount) { 26 this.path = path; 27 this.targetFilePath = targetFilePath; 28 this.threadCount = threadCount; 29 } 30 31 /** 32 * 下载文件 33 */ 34 public void download() throws Exception{ 35 //连接资源 36 URL url = new URL(path); 37 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 38 connection.setRequestMethod("GET"); 39 connection.setConnectTimeout(10000); 40 41 int code = connection.getResponseCode(); 42 if(code == 200){ 43 //获取资源大小 44 int connectionLength = connection.getContentLength(); 45 System.out.println(connectionLength); 46 //在本地创建一个与资源同样大小的文件来占位 47 RandomAccessFile randomAccessFile = new RandomAccessFile(new File(targetFilePath,getFileName(url)), "rw"); 48 randomAccessFile.setLength(connectionLength); 49 /* 50 * 将下载任务分配给每个线程 51 */ 52 int blockSize = connectionLength/threadCount;//计算每个线程理论上下载的数量. 53 for(int threadId = 0; threadId < threadCount; threadId++){//为每个线程分配任务 54 int startIndex = threadId * blockSize; //线程开始下载的位置 55 int endIndex = (threadId+1) * blockSize -1; //线程结束下载的位置 56 if(threadId == (threadCount - 1)){ //如果是最后一个线程,将剩下的文件全部交给这个线程完成 57 endIndex = connectionLength - 1; 58 } 59 60 new DownloadThread(threadId, startIndex, endIndex).start();//开启线程下载 61 62 } 63 } 64 65 } 66 67 //下载的线程 68 private class DownloadThread extends Thread{ 69 70 private int threadId; 71 private int startIndex; 72 private int endIndex; 73 74 public DownloadThread(int threadId, int startIndex, int endIndex) { 75 this.threadId = threadId; 76 this.startIndex = startIndex; 77 this.endIndex = endIndex; 78 } 79 80 @Override 81 public void run() { 82 System.out.println("线程"+ threadId + "开始下载"); 83 try { 84 //分段请求网络连接,分段将文件保存到本地. 85 URL url = new URL(path); 86 87 //加载下载位置的文件 88 File downThreadFile = new File(targetFilePath,"downThread_" + threadId+".dt"); 89 RandomAccessFile downThreadStream = null; 90 if(downThreadFile.exists()){//如果文件存在 91 downThreadStream = new RandomAccessFile(downThreadFile,"rwd"); 92 String startIndex_str = downThreadStream.readLine(); 93 if(null==startIndex_str||"".equals(startIndex_str)){ //网友 imonHu 2017/5/22 94 this.startIndex=startIndex; 95 }else{ 96 this.startIndex = Integer.parseInt(startIndex_str)-1;//设置下载起点 97 } 98 }else{ 99 downThreadStream = new RandomAccessFile(downThreadFile,"rwd"); 100 } 101 102 HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 103 connection.setRequestMethod("GET"); 104 connection.setConnectTimeout(10000); 105 106 //设置分段下载的头信息。 Range:做分段数据请求用的。格式: Range bytes=0-1024 或者 bytes:0-1024 107 connection.setRequestProperty("Range", "bytes="+ startIndex + "-" + endIndex); 108 109 System.out.println("线程_"+threadId + "的下载起点是 " + startIndex + " 下载终点是: " + endIndex); 110 111 if(connection.getResponseCode() == 206){//200:请求全部资源成功, 206代表部分资源请求成功 112 InputStream inputStream = connection.getInputStream();//获取流 113 RandomAccessFile randomAccessFile = new RandomAccessFile(new File(targetFilePath,getFileName(url)), "rw");//获取前面已创建的文件. 114 randomAccessFile.seek(startIndex);//文件写入的开始位置. 115 116 117 /* 118 * 将网络流中的文件写入本地 119 */ 120 byte[] buffer = new byte[1024]; 121 int length = -1; 122 int total = 0;//记录本次下载文件的大小 123 while((length = inputStream.read(buffer)) > 0){ 124 randomAccessFile.write(buffer, 0, length); 125 total += length; 126 /* 127 * 将当前现在到的位置保存到文件中 128 */ 129 downThreadStream.seek(0); 130 downThreadStream.write((startIndex + total + "").getBytes("UTF-8")); 131 } 132 133 downThreadStream.close(); 134 inputStream.close(); 135 randomAccessFile.close(); 136 cleanTemp(downThreadFile);//删除临时文件 137 System.out.println("线程"+ threadId + "下载完毕"); 138 }else{ 139 System.out.println("响应码是" +connection.getResponseCode() + ". 服务器不支持多线程下载"); 140 } 141 } catch (Exception e) { 142 e.printStackTrace(); 143 } 144 145 } 146 } 147 148 //删除线程产生的临时文件 149 private synchronized void cleanTemp(File file){ 150 file.delete(); 151 } 152 153 //获取下载文件的名称 154 private String getFileName(URL url){ 155 String filename = url.getFile(); 156 return filename.substring(filename.lastIndexOf("/")+1); 157 } 158 159 public static void main(String[] args) { 160 String path = "http://img07.tooopen.com/images/20170316/tooopen_sy_201956178977.jpg"; 161 String targetFilePath="d:/"; //下载文件存放目录 162 int threadCount = 3; 163 try { 164 new MultiThreadDown(path, targetFilePath, threadCount).download(); 165 166 } catch (Exception e) { 167 e.printStackTrace(); 168 } 169 } 170 }