Java多线程断点下载
多线程下载已经提高了下载的效率,但是当一些特殊情况发生的时候,我们需要对程序进行处理,这样效率会更高。比如,断电断网等造成下载中断,那么我们下一次又要重新开始下载,这样效率底下,所以我们可以考虑使用断点下载。其原理主要是把每次每个线程的下载状况(已经下载的位置)保存到文件,下次读取出来,从上一次下载的位置继续下载,这样就大大提高了下载的效率。
效果:
开始下载:
下载过程中:
下载过程中,系统临时文件保存已下载的位置:
下载完毕,系统清楚记录下载位置的临时文件:
附代码如下:
1 import java.io.File; 2 import java.io.FileInputStream; 3 import java.io.InputStream; 4 import java.io.RandomAccessFile; 5 import java.net.HttpURLConnection; 6 import java.net.URL; 7 8 /** 9 * 多线程断点下载示例 10 * @author YUANYUAN 11 * 12 */ 13 public class Demo { 14 //下载所使用的线程数 15 private static int threadCount=3; 16 //当前活动的线程数 17 private static int activeThread; 18 19 public static void main(String[] args) throws Exception{ 20 //请求服务器的路径 21 String path="http://192.168.2.114:8080/sqlite.exe"; 22 //构造URL地址 23 URL url=new URL(path); 24 //打开连接 25 HttpURLConnection conn=(HttpURLConnection) url.openConnection(); 26 //设置请求超时的时间 27 conn.setConnectTimeout(5000); 28 //设置请求方式 29 conn.setRequestMethod("GET"); 30 //获取相应码 31 int code=conn.getResponseCode(); 32 if (code==200) {//请求成功 33 //获取请求数据的长度 34 int length=conn.getContentLength(); 35 //在客户端创建一个跟服务器文件大小相同的临时文件 36 RandomAccessFile raf=new RandomAccessFile("setup.exe", "rwd"); 37 //指定临时文件的长度 38 raf.setLength(length); 39 raf.close(); 40 //假设3个线程去下载资源 41 //平均每一个线程要下载的文件的大小 42 int blockSize=length/threadCount; 43 for (int threadId = 1; threadId <= threadCount; threadId++) { 44 //当前线程下载数据的开始位置 45 int startIndex=blockSize*(threadId-1); 46 //当前线程下载数据的结束位置 47 int endIndex=blockSize*threadId-1; 48 //确定最后一个线程要下载数据的最大位置 49 if (threadId==threadCount) { 50 endIndex=length; 51 } 52 //显示下载数据的区间 53 System.out.println("线程【"+threadId+"】开始下载:"+startIndex+"---->"+endIndex); 54 //开启下载的子线程 55 new DownloadThread(path, threadId, startIndex, endIndex).start(); 56 activeThread++; 57 System.out.println("当前活动的线程数:"+activeThread); 58 } 59 60 }else{//请求失败 61 System.out.println("服务器异常,下载失败!"); 62 } 63 } 64 65 /** 66 * 下载文件的子线程 每一个文件都下载对应的数据 67 * @author YUANYUAN 68 * 69 */ 70 public static class DownloadThread extends Thread{ 71 private String path; 72 private int threadId; 73 private int startIndex; 74 private int endIndex; 75 76 /** 77 * 构造方法 78 * @param path 下载文件的路径 79 * @param threadId 下载文件的线程 80 * @param startIndex 下载文件开始的位置 81 * @param endIndex 下载文件结束的位置 82 */ 83 public DownloadThread(String path, int threadId, int startIndex, 84 int endIndex) { 85 this.path = path; 86 this.threadId = threadId; 87 this.startIndex = startIndex; 88 this.endIndex = endIndex; 89 } 90 91 92 93 @Override 94 public void run() { 95 //构造URL地址 96 try { 97 98 File tempFile=new File(threadId+".txt"); 99 //检查记录是否存在,如果存在读取数据 100 if (tempFile.exists()) { 101 FileInputStream fis=new FileInputStream(tempFile); 102 byte[] temp=new byte[1024]; 103 int length=fis.read(temp); 104 //读取到已经下载的位置 105 int downloadNewIndex=Integer.parseInt(new String(temp, 0, length)); 106 //设置重新开始下载的开始位置 107 startIndex=downloadNewIndex; 108 fis.close(); 109 //显示真实下载数据的区间 110 System.out.println("线程【"+threadId+"】真实开始下载数据区间:"+startIndex+"---->"+endIndex); 111 } 112 113 URL url = new URL(path); 114 HttpURLConnection conn=(HttpURLConnection) url.openConnection(); 115 conn.setConnectTimeout(5000); 116 conn.setRequestMethod("GET"); 117 //设置请求属性,请求部分资源 118 conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex); 119 int code=conn.getResponseCode(); 120 if (code==206) {//下载部分资源,正常返回的状态码为206 121 InputStream is=conn.getInputStream();//已经设置了请求的位置,所以返回的是对应的部分资源 122 //构建随机访问文件 123 RandomAccessFile raf=new RandomAccessFile("setup.exe", "rwd"); 124 //设置 每一个线程随机写文件开始的位置 125 raf.seek(startIndex); 126 //开始写文件 127 int len=0; 128 byte[] buffer=new byte[1024]; 129 //该线程已经下载数据的长度 130 int total=0; 131 132 while((len=is.read(buffer))!=-1){//读取输入流 133 //记录当前线程已下载数据的长度 134 RandomAccessFile file=new RandomAccessFile(threadId+".txt","rwd"); 135 raf.write(buffer,0,len);//写文件 136 total+=len;//更新该线程已下载数据的总长度 137 System.out.println("线程【"+threadId+"】已下载数据:"+(total+startIndex)); 138 //将已下载数据的位置记录写入到文件 139 file.write((startIndex+total+"").getBytes()); 140 file.close(); 141 } 142 is.close(); 143 raf.close(); 144 //提示下载完毕 145 System.out.println("线程【"+threadId+"】下载完毕"); 146 } 147 } catch (Exception e) { 148 e.printStackTrace(); 149 System.out.println("线程【"+threadId+"】下载出现异常!!"); 150 }finally{ 151 //活动的线程数减少 152 activeThread--; 153 if (activeThread==0) { 154 for (int i = 1; i <= threadCount; i++) { 155 File tempFile=new File(i+".txt"); 156 tempFile.delete(); 157 } 158 System.out.println("下载完毕,已清除全部临时文件"); 159 } 160 } 161 162 } 163 } 164 }