多线程断点下载工具

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/MrwanZH/article/details/75950355

多线程断点下载工具需要完成以下几个功能:

        * 1.通过提供的网络文件地址,下载該文件
        * 2.要求使用多个线程同时下载,并且实时更新下载进度(0%~100%)
        * 3.要求能够实现断点功能(扩展)


实现思想:

        1.创建多个下载线程和一个进度线程,利用线程名字区分两类线程。以下内容以四个下载线程为例。

        2.根据下载链接构建一个URL对象,根据此URL对象打开一个HttpURLConnection链接对象conn,然后调用conn.getContentLength()获得下载文件的大小。

        3.将文件大小平分四份设为每个线程的下载任务,并且记录每个文件下载的起始和终止位置,该量为long类型。

        4.重写线程类的run方法,添加switch函数以区分下载线程和查看进度线程的任务,其中线程名字为switch的参数。

        5.下载文件时要先调用readPosition函数来查看文件当前下载位置,此处的查看地点是自己创建的txt文件,txt文件的内容是实时跟新的当前已下载部分的最末尾。然后线程开始自己的下载任务。读文件采用BufferedInputStream流,因为HttpURLConnection.getInputStream()的参数为字节流,所以用BufferedInputStream高级字节流来包装被下载文件的字节内容。写文件采用RandomAccessFile流,因为需要完成断点拷贝任务,而RandomAccessFile流可以调用seek()函数来完成移动指针的目的,从而完成在指定位置开始写。线程完成了当前读写任务后,则需要调用writePosition函数将自己当前的读写末尾位置写入一个txt文件中,以实现断点下载 的目的,即下一次下载时线程能读到此次写入的末尾,从而从上一次的末尾继续往后下载,所以每次下载时要调用readPosition函数来查看文件当前下载位置。

        6.其次是进度函数。进度函数会一直查看已下载的大小,和文件总大小进行对比得到文件下载进度。



以下是运行效果:


首次执行:

终止程序,再次执行后,完成断点任务:


完成 下载:


记录文件长度的len文本:


以下是源代码:


package HomeWork;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DecimalFormat;

public class DownloadFromNet extends Thread{
	/**
	 * 完成一个多线程断点下载工具
	 * 1.通过提供的网络文件地址,下载該文件
	 * 2.要求使用多个线程同时下载,并且实时更新下载进度(0%~100%)
	 * 3.要求能够实现断点功能(扩展)
	 * @throws IOException 
	 */
	private boolean isExit = false; // 控制进度线程,下载进度100%则赋值true,线程终止
	private String s;               // 下载文件地址 http 
	private File dir; 				// 下载路径
	private long start;				// 进程开始下载下标
	private long end;				// 进程结束下载下标
	private long totalLength;  		// 下载文件的大小
	private int i;					// 进程ID

	public DownloadFromNet(){
	}
	
	public DownloadFromNet(String s, long totalLength, String name){
		super(name);
		this.s = s;
		this.totalLength = totalLength;
	}

	public DownloadFromNet(String s, File dir , long totalLength, long start, long end, String name, int i) {
		super(name);
		this.s = s;
		this.dir = dir; 
		this.totalLength = totalLength;
		this.start = start;
		this.end = end;
		this.i = i;
	}
	
	
	@Override
	public void run() {
		
		switch(this.getName()){ // 根据线程名字进行分配
		case "下载":
			try {
				download();
			} catch (IOException | InterruptedException e) {
				e.printStackTrace();
			}
			break;
		case "进度":		
			progress();
			break;	
		}
		
		
		
	}		
	public void download() throws IOException, InterruptedException{
	
		HttpURLConnection conn = creatConn(s);// 重新连接  不要一直用一个  如 主函数传conn进来
		RandomAccessFile raf_write = null;
		BufferedInputStream bis = null;
		long length = 0;
		//读
		//获取响应的状态码
		int code = conn.getResponseCode();
		//判断是否响应成功
		if(code == HttpURLConnection.HTTP_OK){
			if (!new File(dir, "len.txt").exists())// 在这里判断是不是存在  不存在在这里读一次  调用writePosition
				for (int i = 0; i < 4; ++i)
					writePosition(0L, i);
			
			length = readPosition(i);
			if (i > 1 && length == 0L)  	  // 如果是下载线程是第一次执行且下载任务不是下载文件首端的,则为其赋初始值start
				length = start;
			
			// 开启读 
			InputStream is = conn.getInputStream();
			bis = new BufferedInputStream(is);
			//准备目标文件
			File f = new File("C:\\Users\\admin\\Desktop",s.substring(s.lastIndexOf("/")));
		
			//以读写模式开启一个文件访问流
			raf_write = new RandomAccessFile(f, "rw");
			
			//读写指针跳到指定位置
			bis.skip(length);
			raf_write.seek(length);
			
			byte[] b = new byte[1024];// 1k
			int len = 0; 
			for(int i = 0;((len = bis.read(b)) != -1 && length <= end); i++)
			{
				sleep(100);
				length += len;
				raf_write.write(b, 0, len);
				writePosition(length, i);
			}
			raf_write.close();
			bis.close();
		}
	}
	
	public long readPosition(int i) throws IOException{
		RandomAccessFile raf = null;
		try {
			raf = new RandomAccessFile(new File("C:\\Users\\admin\\Desktop\\Test\\len.txt"), "rw");
			raf.seek(8 * i);
			System.out.println(raf.readLong());
		} catch (FileNotFoundException e) {	
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		finally{
			try {
				if(raf != null)raf.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return 0L;
	}
	/**
	 * 实时记录文件拷贝(下载)到的目标位置
	 * @param position
	 * @param dir
	 */
	public void writePosition(long position, int i){ // position文件大小, i是线程数
		RandomAccessFile raf = null;
		try {
			raf = new RandomAccessFile(new File(dir,"len.txt"),"rw");
			raf.seek(8 * i);	// 每个线程存储position的大小为八个字节
			raf.writeLong(position);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}finally{
			try {
				if(raf != null)raf.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	
	public void progress(){ // 输出下载进度函数
		DecimalFormat bd = new DecimalFormat("##.##%");
		RandomAccessFile raf_read2 = null;
		long Length2;
		long Length3;
		while (!isExit){
			try {
				File file = new File("C:\\Users\\admin\\Desktop", s.substring(s.lastIndexOf("/")));
				raf_read2 = new RandomAccessFile(file, "rw");
				Length2 = raf_read2.length();
				 
				Length3 = totalLength;
				
				if (Length2 == Length3) isExit = true;
				System.out.println("进度:" + bd.format(1.0 * Length2 / Length3));
				try {
					sleep(500);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} finally {
					try {
						if (raf_read2 != null)
						raf_read2.close();
					} catch (IOException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
			}
		}
		System.out.println("下载完成");
	}
		
	public static void main(String[] args) throws IOException {
		
		String s = "http://dynamic-image.yesky.com/740x-/uploadImages/2016/337/54/492907OO7E5I.jpg";	
		HttpURLConnection conn = DownloadFromNet.creatConn(s);

		long len = conn.getContentLength();
		File dir = new File("C:\\Users\\admin\\Desktop");
		long position2 = len / 4;
		long position3 = position2 * 2;
		long position4 = position2 * 3; 
		
		DownloadFromNet dfn1 = new DownloadFromNet(s, dir, len, 0L, position2, "下载", 0);
		DownloadFromNet dfn2 = new DownloadFromNet(s, dir, len, position2, position3, "下载", 1);
		DownloadFromNet dfn3 = new DownloadFromNet(s, dir, len, position3, position4, "下载", 2);
		DownloadFromNet dfn4 = new DownloadFromNet(s, dir, len, position4, len, "下载", 3);
		DownloadFromNet dfn5 = new DownloadFromNet(s, len, "进度");
		dfn1.start();
		dfn2.start();
		dfn3.start();
		dfn4.start();
		dfn5.start();

	}

	private static HttpURLConnection creatConn(String string) throws IOException {
	
		//根据字符串构建一个URL对象
		URL url = new URL(string);
		//打开一个URL链接
		HttpURLConnection conn = (HttpURLConnection)url.openConnection();
		//设置请求方法(get请求)
		conn.setRequestMethod("GET");
		//设置读取超时时间
		conn.setReadTimeout(10000); 
		
		return conn;
	}
}

猜你喜欢

转载自blog.csdn.net/MrwanZH/article/details/75950355
今日推荐