Java多线程下载和断点续传
谈谈的心情 人气:0java的多线程下载能够明显提升下载的速度,平时我们用的迅雷软件之所以能够下载那么快,就是使用了多线程;当用户在下载的过程中,有断电或断网的可能,当用户再次点击下载时,应该让用户接着原来的进度进行下载,这可以节约用户的流量,所以要用到断点续传的功能。下面是通过Java代码实现多线程下载和断点续传的详细代码。
1、创建一个类,用于文件的下载
package com.edu.thread; 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; public class MultiDownload2 { static String path = "http://localhost:8080/wlan.zip"; //开启线程的数量 static int threadCount = 6; //下载结束的线程数 static int threadFinished = 0; public static void main(String[] args) { 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("d://文件测试", getFileName(path)); //创建随机存储文件大小,为了建立一个和源文件大小相同的存储区间 RandomAccessFile raf = new RandomAccessFile(file, "rwd"); //设置临时文件的大小,和服务器文件一模一样 raf.setLength(length); //计算每个线程下载的字节数 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) { //length从0开始读,所以length-1表示最后一个字节 endIndex = length-1; } //打印三个线程的开始与结束位置 System.out.println("线程"+i+"的开始和结束位置:"+startIndex+"----"+endIndex); //开启线程,传入线程ID,下载的开始位置和下载的结束位置 new DownloadThread(i, startIndex, endIndex).start();; } } } catch (Exception e) { e.printStackTrace(); } } /* * 获取文件名 */ public static String getFileName(String path){ int index=path.lastIndexOf("/"); return path.substring(index + 1); } }
2、创建另一个类,用于开启子线程
package com.edu.thread; 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; //新开启一个线程,用于完成下载任务 class DownloadThread extends Thread{ int thredId; int startIndex; int endIndex; public DownloadThread(int thredId, int startIndex, int endIndex) { super(); this.thredId = thredId; this.startIndex = startIndex; this.endIndex = endIndex; } public void run() { try { //下载进度文件保存的路径和文件名 File progressFile = new File("d://文件测试",(thredId + ".txt")); //判断保存下载进度的临时文件是否存在,以便确定下载的开始位置 if (progressFile.exists()) { FileInputStream fis = new FileInputStream(progressFile); BufferedReader bReader = new BufferedReader(new InputStreamReader(fis)); //拿到临时文件中保存的数据,并把此数据设置为新的开始位置 int text = Integer.parseInt(bReader.readLine()); startIndex = text; fis.close(); } System.out.println("线程"+thredId+"的最终开始下载位置是:"+startIndex); URL url = new URL(MultiDownload2.path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000); conn.setReadTimeout(5000); //设置请求数据的范围 conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex); //建立连接,状态码206表示请求部分数据成功,此时开始下载任务 if (conn.getResponseCode()==206) { InputStream is = conn.getInputStream(); //指定文件名和文件路径 File file = new File(MultiDownload2.getFileName(MultiDownload2.path) ); int len = 0; byte [] b = new byte[1024]; //三个线程各自创建自己的随机存储文件 RandomAccessFile raf = new RandomAccessFile(file, "rwd"); //设置数据从哪个位置开始写入数据到临时文件 raf.seek(startIndex); //设置当前线程下载的总字节数 int total = 0; long start = System.currentTimeMillis(); //当下载意外停止时,记录当前下载进度 int currentPosition = startIndex; while ((len=is.read(b))!=-1) { raf.write(b,0,len); //打印当前线程下载的总字节数 total += len; /** * 实现断点续传的功能 */ //RandomAccessFile主要用来存放下载的临时文件,可以用FileOutputStream代替 RandomAccessFile rafProgress = new RandomAccessFile(progressFile, "rwd"); //再次下载时的开始位置 currentPosition = startIndex + total; //把下载进度写进rafProgress临时文件,下一次下载时,就以这个值作为新的startIndex rafProgress.write((currentPosition + "").getBytes()); //关流 rafProgress.close(); System.out.println("线程"+thredId+"已经下载了"+total); } raf.close(); long end = System.currentTimeMillis(); //打印线程下载文件用时 System.out.println("线程"+thredId+"下载文件用时"+(end-start)+"ms"); //打印线程的结束 System.out.println("线程:"+thredId+" 下载结束了 !!!"); //下载结束后,删除所有的临时文件 MultiDownload2.threadFinished ++; //使用同步语句块,保证线程的安全性 synchronized (MultiDownload2.path) { //如果这个条件成立,说明所有的线程下载结束 if (MultiDownload2.threadFinished == MultiDownload2.threadCount) { for (int i = 0; i < MultiDownload2.threadCount; i++) { //删除三个线程产生的临时文件 File temp = new File("d://文件测试", i + ".txt"); temp.delete(); } //保证三个线程的临时文件同时被删除 MultiDownload2.threadFinished = 0; } } } } catch (Exception e) { e.printStackTrace(); } } }
加载全部内容