Java实现多线程下载和断点续传

14 篇文章 0 订阅

java的多线程下载能够明显提升下载的速度,平时我们用的迅雷软件之所以能够下载那么快,就是使用了多线程;当用户在下载的过程中,有断电或断网的可能,当用户再次点击下载时,应该让用户接着原来的进度进行下载,这可以节约用户的流量,所以要用到断点续传的功能。下面是通过Java代码实现多线程下载和断点续传的详细代码。

1,创建一个类,用于文件的下载

<pre name="code" class="java"><span style="font-size:18px;">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);
	}
}</span>
 

2,创建另一个类,用于开启子线程

<pre name="code" class="java"><span style="font-size:18px;">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();
		}
	}
}</span>
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值