import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; /** * 多线程断点续传的原理 * 1、当多线程突然中断的时候,在缓存文件中保存中断的位置; * 2、当续传的时候,分别读取缓存文件中的位置,然后作为开始的位置下载。 * @author Sheamus * */ public class MutilHttpDownload { //完成下载的线程数 static int finishedThread = 0; static String path = "http://10.31.2.6:8080/demo/QQ.exe"; static int PROCESSNUM = 3; public static void main(String[] args) { //请求文件的大小并创建一个一样大小的缓存文件 try { URL url = new URL(path); //在项目的目录下创建一个QQ.exe文件 File file = new File("QQ.exe"); RandomAccessFile raf = new RandomAccessFile(file, "rwd"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setReadTimeout(5000); conn.setReadTimeout(5000); if(conn.getResponseCode() == 200) { int length = conn.getContentLength(); //设置缓存文件的大小 raf.setLength(length); raf.close(); //计算每个线程应该下载的大小 int size = length / PROCESSNUM; //计算每个线程开始和结束的位置 for(int i = 0; i < PROCESSNUM; i++) { int startIndex = i * size; int endIndex = (i + 1) * size - 1; //当时最后一个线程的时候,最后的位置就是总长度减1 if(i == (PROCESSNUM - 1)) { endIndex = length - 1; } //启动线程执行下载 new SingleDownLoadThread(startIndex,endIndex,i).start(); } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } class SingleDownLoadThread extends Thread { private int startIndex; private int endIndex; private int currentThreadId; public SingleDownLoadThread(int startIndex, int endIndex, int currentThreadId) { super(); this.startIndex = startIndex; this.endIndex = endIndex; this.currentThreadId = currentThreadId; } @Override public String toString() { return "DownLoadThread [startIndex=" + startIndex + ", endIndex=" + endIndex + ", currentThreadId=" + currentThreadId + "]"; } @Override public void run() { int total = 0; //下载该线程的要下载的部分 try { //定义缓存文件用于保存下载的位置 File tempFile = new File("temp" + currentThreadId +".txt"); if(tempFile.exists()) { //下载的时候,如果有缓存文件,读取缓存中文件,将这个数字替换开始的位置 FileInputStream fis = new FileInputStream(tempFile); BufferedReader bis = new BufferedReader(new InputStreamReader(fis)); int tempStartIndex = Integer.parseInt(bis.readLine()); startIndex = tempStartIndex; fis.close(); } System.out.println("下载的区间是 :" + startIndex +" -- > " + endIndex +"--------------------------------"); File file = new File("QQ.exe"); RandomAccessFile raf = new RandomAccessFile(file, "rwd"); URL url = new URL(MutilHttpDownload.path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); //设置要下载的部分 conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex); //将随机读取读取文件的流的指针指向要读文件的开始位置 raf.seek(startIndex); //部分文件获取成功的返回码是206 if(conn.getResponseCode() == 206) { InputStream in = conn.getInputStream(); byte[] b = new byte[1024]; int len = 0; while((len = in.read(b)) != -1) { raf.write(b, 0, len); total += len; System.out.println("线程 " + currentThreadId + "下载了 " + total); //创建一个输出流写出当前读到的位置 RandomAccessFile tempRaf = new RandomAccessFile(tempFile, "rwd"); tempRaf.write((total+"").getBytes()); tempRaf.close(); } System.out.println("线程 " + currentThreadId + "下载完成!"); raf.close(); MutilHttpDownload.finishedThread++; synchronized (MutilHttpDownload.path) { if(MutilHttpDownload.finishedThread == MutilHttpDownload.PROCESSNUM) { for(int i = 0; i < MutilHttpDownload.PROCESSNUM; i++) { File f = new File("temp" + i + ".txt"); System.out.println("删除" + f.getName() +"文件"); f.delete(); } //只让一个线程进来删除文件,要不然会出异常 MutilHttpDownload.finishedThread = 0; } } } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
原文:http://my.oschina.net/Sheamus/blog/493418