简易多线程下载

多线程断点下载原理:

1.设置Range请求头,开启子线程后,各归各下载相应的数据

2.下载的过程中,每个线程都有个文件实时记录当前下载进度,保证暂停下载后再次继续可以获取上次下载的进度。




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;

/**
 * 
 * @author 多线程断点下载
 *
 */
public class ManyThreadDownload {
	private static int threadTotalCount = 3;							//总共开几个线程运行
	private static String path = "http://127.0.0.1:9090/555.exe";		//下载地址
	private static int runningThreadCount;								//当前正在运行的线程个数

	public static void main(String[] args) {
		HttpURLConnection conn = null;
		try {
			URL url = new URL(path);									//URL地址
			conn = (HttpURLConnection) url.openConnection();			//获取一个连接对象
			conn.setConnectTimeout(3000);								//设置连接超时时间
			conn.setReadTimeout(3000);									//设置已经连上服务器,但是在读取数据时候的超时时间
			conn.setRequestMethod("GET");								//设置请求方式
			if (conn.getResponseCode() == 200) {						//请求成功后
				int size = conn.getContentLength();						//获取服务器返回的文件大小
				long blockSize = size / threadTotalCount;				//每个线程要下载的大小=服务器文件大小/线程数
				runningThreadCount = threadTotalCount;					//刚开始的时候将线程总数赋值给正在运行的线程数
				for (int i = 0; i < threadTotalCount; i++) {			//循环开启线程下载
					long startIndex = i * blockSize;					//计算每个线程开始位置下载的位置
					long endIndex = (i + 1) * blockSize - 1;			//计算每个线程结束位置下载的位置
					if (i == (threadTotalCount - 1)) {					//当循环到最后一个线程的时候,下载的结束位置就是文件大总大小
						endIndex = size;
					}
					new DownLoadThread(i, path, startIndex, endIndex).start();//进行下载
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (conn != null) {
				conn.disconnect();
			}
		}
	}

	private static class DownLoadThread extends Thread {
		private int threadIndex;						//当前线程索引号
		private String path;							//下载文件的url
		private long startIndex;						//开始下载的位置
		private long endIndex;							//下载到哪个位置结束

		public DownLoadThread(int threadIndex, String path, long startIndex, long endIndex) {
			this.threadIndex = threadIndex;
			this.path = path;
			this.startIndex = startIndex;
			this.endIndex = endIndex;
		}

		@Override
		public void run() {
			super.run();
			HttpURLConnection conn = null;
			File currentPositionFile = new File("Thread"+threadIndex+".txt");			//这个文件是用于记录每个线程下载进度的一个文件,只保存下载的字节数
			/* RandomAccessFile
			 * 用这个类来操作保存下载进度的文件,因为将这个类第二个参数设置rwd模式的话,会实时更新硬盘上的数据,而如果用FileOutputStream的话,就算调用flush,也只是刷到硬盘上的缓存里,
			 * 并没有马上写到硬盘上
			 */
			RandomAccessFile currentPositionRAF = null;									
			int total = 0;																//当前下载的字节进度	
			try {
				if(currentPositionFile.exists()&¤tPositionFile.length()>0){		//如果记录进度的文件存在的话,就表示之前暂停过
					FileInputStream fis = new FileInputStream(currentPositionFile);		
					InputStreamReader isr = new InputStreamReader(fis);
					BufferedReader br = new BufferedReader(isr);
					String lastPosition = br.readLine();
					if(lastPosition!=null&&!"".equals(lastPosition.trim())){		    //读取进度文件,如果读到的内容不为空
						total = Integer.parseInt(lastPosition);							//将当前线程的总进度设置为进度文件里的进度	
						startIndex += Integer.valueOf(lastPosition);					//将当前开始进度设置为进度文件里的进度(因为是断点下载)
						System.out.println("线程"+threadIndex+",从"+lastPosition+"继续开始");
					}
					fis.close();														//千万不要忘记关闭流
					isr.close();														//千万不要忘记关闭流
					br.close();															//千万不要忘记关闭流
				}	
				
				URL url = new URL(path);												//URL地址
				conn = (HttpURLConnection) url.openConnection();						//获取一个连接对象
				conn.setConnectTimeout(3000);											//设置连接超时时间
				conn.setReadTimeout(3000);												//设置已经连上服务器,但是在读取数据时候的超时时间
				conn.setRequestMethod("GET");											//设置请求方式
				conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);		//设置消息头是断点下载,告诉服务器返回指定片段数据
				
				if(conn.getResponseCode()==206){										//带着Range消息头去访问服务器,服务器返回的状态码是206
					InputStream is = conn.getInputStream();								//获取服务器返回数据的输入流
					File file = new File("hehe.exe");									//创建本地文件
					RandomAccessFile raf = new RandomAccessFile(file, "rw");			//创建一个往下载文件里写数据的类
					raf.seek(startIndex);												//设置上次的进度
					int len = 0;														
					byte[] buffer = new byte[1024*1024];
					while((len=is.read(buffer))!=-1){
						total += len;
						currentPositionRAF = new RandomAccessFile(currentPositionFile, "rwd");
						currentPositionRAF.write(String.valueOf(total).getBytes());		//写进度
						raf.write(buffer, 0, len);
						//记录文件下载进度的txt文件必须是循环里写一次关一次,因为JVM关闭的时候,不一定是循环完毕的时候,这时候进度文件还是打开着的
						currentPositionRAF.close();				
					}
					is.close();
					raf.close();
				}
			} catch (Exception e) {
				e.printStackTrace();
			} finally {
				synchronized (ManyThreadDownload.class) {
					if(conn!=null){
						conn.disconnect();
					}
					//因为执行到finally时候,肯定是进程已经下载完毕了,所以把当前在运行的进程数减一
					runningThreadCount--;
					if(runningThreadCount==0){
						for (int i = 0; i < threadTotalCount; i++) {			//删除记录每个线程进度的临时文件
							File file = new File("Thread"+i+".txt");
							try {
								System.out.println(file.delete());
							} catch (Exception e) {
								e.printStackTrace();
							}
						}
					}
				}
			}
		}
	}
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值