原理
将要已经下载进度保存到临时文件中,例如:下载了3000字节的话,也就是保存了0~2999的位置,下次从3000的位置开始继续下载,那么把3000保存到临时文件中即可。
对于多线程的话,我们也要分别进行记录,有几个线程则保存几个文件,
例如
0号线程:下载了6000字节 记录 0~5999 下次从6000字节开始
1号线程:下载了4000字节 记录 0~3999 下次从4000字节开始
2号线程:下载了500字节 记录 0~499 下次从500字节开始
计算:
例如:
下载区间为100-300
已经下载120 即 100-219
下次下载应该从220开始下载。
即:newStartIdx =startInx + total
带断点续传的多线程下载
定义一个int变量记录每条线程下载的数据总长度,然后加上该线程的下载开始位置,得到的结果就是下次下载时,该线程的开始位置,把得到的结果存入缓存文件
//用来记录当前线程总的下载长度 int total = 0; while((len = is.read(b)) != -1){ raf.write(b, 0, len); total += len; //每次下载都把新的下载位置写入缓存文本文件 RandomAccessFile raf2 = new RandomAccessFile(threadId + ".txt", "rwd"); raf2.write((startIndex + total + "").getBytes()); raf2.close(); }
下次下载开始时,先读取缓存文件中的值,得到的值就是该线程新的开始位置
FileInputStream fis = new FileInputStream(file); BufferedReader br = new BufferedReader(new InputStreamReader(fis)); String text = br.readLine(); int newStartIndex = Integer.parseInt(text); //把读到的值作为新的开始位置 startIndex = newStartIndex; fis.close();
三条线程都下载完毕之后,删除缓存文件
RUNNING_THREAD--; if(RUNNING_THREAD == 0){ for(int i = 0; i <= 3; i++){ File f = new File(i + ".txt"); f.delete(); } }
核心代码
public class MultiDownload { static int ThreadCount = 3; static int finishedThread = 0; //确定下载地址 static String path = "http://192.168.1.13:8080/QQ.exe"; public static void main(String[] args) { //发送get请求,请求这个地址的资源 try { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); if(conn.getResponseCode() == 200){ //拿到所请求资源文件的长度 int length = conn.getContentLength(); File file = new File("local_qq.exe"); //生成临时文件--占用磁盘空间--可以防止磁盘空间不够用 RandomAccessFile raf = new RandomAccessFile(file, "rwd"); //设置临时文件的大小 raf.setLength(length); raf.close(); //计算出每个线程应该下载多少字节 int size = length / ThreadCount; for (int i = 0; i < ThreadCount; i++) { //计算线程下载的开始位置和结束位置 int startIndex = i * size; int endIndex = (i + 1) * size - 1; //如果是最后一个线程,那么结束位置写死 if(i == ThreadCount - 1){ endIndex = length - 1; } // System.out.println("线程" + i + "的下载区间是:" + startIndex + "---" + endIndex); new DownLoadThread(startIndex, endIndex, i).start(); } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } class DownLoadThread extends Thread{ int startIndex; //下载的开始位置 int endIndex; //下载的结束位置 int threadId; //下载的线程Id public DownLoadThread(int startIndex, int endIndex, int threadId) { super(); this.startIndex = startIndex; this.endIndex = endIndex; this.threadId = threadId; } @Override public void run() { //再次发送http请求,下载原文件 try { File progressFile = new File(threadId + ".txt"); //判断进度临时文件是否存在 if(progressFile.exists()){ FileInputStream fis = new FileInputStream(progressFile); BufferedReader br = new BufferedReader(new InputStreamReader(fis)); //从进度临时文件中读取出上一次下载的总进度,然后与原本的开始位置相加,得到新的开始位置 startIndex += Integer.parseInt(br.readLine()); fis.close(); } System.out.println("线程" + threadId + "的下载区间是:" + startIndex + "---" + endIndex); HttpURLConnection conn; URL url = new URL(MultiDownload.path); conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); //设置本次http请求所请求的数据的区间 conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex); //请求部分数据,相应码是206 if(conn.getResponseCode() == 206){ //流里此时只有1/3原文件的数据 InputStream is = conn.getInputStream(); byte[] b = new byte[1024]; int len = 0; int total = 0; //拿到临时文件的输出流 File file = new File("local_qq.exe"); RandomAccessFile raf = new RandomAccessFile(file, "rwd"); //把文件的写入位置移动至startIndex raf.seek(startIndex); while((len = is.read(b)) != -1){ //每次读取流里数据之后,同步把数据写入临时文件 raf.write(b, 0, len); total += len; // System.out.println("线程" + threadId + "下载了" + total); //生成一个专门用来记录下载进度的临时文件 RandomAccessFile progressRaf = new RandomAccessFile(progressFile, "rwd"); //每次读取流里数据之后,同步把当前线程下载的总进度写入进度临时文件中 progressRaf.write((total + "").getBytes()); progressRaf.close(); } System.out.println("线程" + threadId + "下载完毕-------------------"); raf.close(); MultiDownload.finishedThread++; synchronized (MultiDownload.path) { if(MultiDownload.finishedThread == MultiDownload.ThreadCount){ for (int i = 0; i < MultiDownload.ThreadCount; i++) { File f = new File(i + ".txt"); f.delete(); } MultiDownload.finishedThread = 0; } } } } catch (Exception e) { e.printStackTrace(); } } }