JAVA多线程断点续传下载

JAVA多线程断点续传下载    


      多个线程同时去下载一个资源,肯定比一个线程去下载一个资源速度快的多,所以现在我们要实现一下多个线程去下载一个资源,而断点下载的意思是,如果上一次因为某种原因,下载没有进行到底,再进行下一次下载的时候,可以在上一次下载的位置继续进行下载,而不需要又从文件的第一个字节开始下载。

下面简单的说一下多线程下载的步骤:

1、通过HttpURLConnection的getContentLength()获取要下载文件的总大小

2、根据所开启的线程的数量,将资源等分,比如这个文件总大小是6.1M,现在开启三个线程A、B、C去下载,所以A线程下载的范围是0~2M,B线程下载的范围是2~4M,C线程下载的范围是4~6.1M,我们可以让最后一个线程辛苦一点,多下0.1M。

3、在本地建立与线程数量对应的临时文件(positionFile)用于记录每个线程下载的位置,这样下次就可以从这个位置继续下载

4、如果每个线程全部下载完毕,则可以删除临时文件。


好了,下面是代码实现。

我把一个叫StringBuffer的视频放在tomcat服务器上,然后开启了4个线程去同时下载这个视频。

public class MutileThreaddownload {
	// 线程的数量 
	private static int threadCount = 4;
	// 每个下载区块的大小
	private static long blockSize;
	// 正在运行的线程的数量
	private static int runningThreadCount;
	
	public static void main(String[] args) {
		try {
			String downloadPath="http://localhost:8080/FormWorkTest/StringBuffer.avi";
			URL downloadUrl=new URL(downloadPath);
			HttpURLConnection coon=(HttpURLConnection) downloadUrl.openConnection();
			coon.setRequestMethod("GET");
			coon.setReadTimeout(5000);
	
			int code = coon.getResponseCode();
			if(code==200){	
				runningThreadCount = threadCount;
				//获得服务器端目标文件的长度
				int fileLength = coon.getContentLength();
				System.out.println("服务器端文件的大小是:"+fileLength);		
				//1、在本地弄一个和目标文件一样大的file对象
				File descFile=new File("temp.avi");
				RandomAccessFile raf=new RandomAccessFile(descFile, "rw");
				raf.setLength(fileLength);
	
				//2、计算出每一个线程应该下载文件的大小
				blockSize=fileLength/threadCount;
				
				//3、开启若干个线程去下载文件
				for (int i = 0; i < threadCount; i++) {
					//先设定每个线程下载的开始位置和结束位置
					long startIndex=i*blockSize;
					long endIndex=(i+1)*blockSize-1;	
					//如果是最后一个线程
					if(i==threadCount-1){
						endIndex=fileLength-1;
					}
					new <span style="color:#ff6666;">DownLoadThread</span>(downloadPath,startIndex,endIndex,i,runningThreadCount,threadCount).start();
				}
				raf.close();
			}
		
			coon.disconnect();
		} catch (Exception e) {
		
			e.printStackTrace();
		}	
	}
}

public class <span style="color:#ff6666;">DownLoadThread </span>extends Thread {
	private long endIndex;
	private long startIndex;
	private String urlPath;
	private int i;
	private static int runningThreadCount;
	private int threadCount;
	
	DownLoadThread(String urlPath,long startIndex,long endIndex ,int i,int runningThreadCount,int threadCount){		
		this.i=i;
		this.endIndex=endIndex;
		this.startIndex=startIndex;
		this.urlPath=urlPath;
		this.runningThreadCount=runningThreadCount;
		this.threadCount=threadCount;
	}

	@Override
	public void run() {
		try {
			long totalSize=0,len=0;
			//建立positionFile,用于存储每个线程下载的文件字节位置
			File positionFile=new File(String.valueOf(i)+".txt");
			// 接着从上一次的位置继续下载数据
			if (positionFile.exists() && positionFile.length() > 0) {// 判断是否有记录
				FileInputStream fis = new FileInputStream(positionFile);
				BufferedReader br = new BufferedReader(new InputStreamReader(fis));
				// 获取当前线程上次下载的总大小是多少
				String lasttotalstr = br.readLine();
				int lastTotal = Integer.valueOf(lasttotalstr);
				System.out.println("上次线程" + i + "下载的总大小:"+ lastTotal);
				startIndex += lastTotal;
				totalSize += lastTotal;// 加上上次下载的总大小。
				fis.close();
			}
			
			URL downLoadUrl=new URL(urlPath);
			HttpURLConnection connection = (HttpURLConnection) downLoadUrl.openConnection();
			connection.setRequestMethod("GET");
			connection.setConnectTimeout(5000);
			connection.setRequestProperty("Range", "bytes=" + startIndex + "-"+ endIndex);
			int code = connection.getResponseCode();
			System.out.println("这是第"+i+"个线程,"+"code=" + code);
			//下载文件的一部分的返回码是206
			if(code==206){		
				InputStream inputStream = connection.getInputStream();
				BufferedInputStream bis=new BufferedInputStream(inputStream);
				
				File tempFile=new File("temp.avi");
				RandomAccessFile raf=new RandomAccessFile(tempFile, "rw");
				//指定文件开始读取的位置
				raf.seek(startIndex);
				System.out.println("这是第"+i+"个线程,下载范围是"+startIndex+"~"+endIndex);
			
				byte[] buffer=new byte[1024];
				while((len=bis.read(buffer))!=-1){
					//这里的模式必须是"rwd"
					RandomAccessFile positionRaf=new RandomAccessFile(positionFile, "rwd");
					totalSize+=len;	
					raf.write(buffer, 0, buffer.length);	
					positionRaf.write(String.valueOf(totalSize).getBytes());
					positionRaf.close();
				}
				
				bis.close();
				raf.close();
			}		
		} catch (Exception e) {		
			e.printStackTrace();
		}finally {
			// 只有所有的线程都下载完毕后 才可以删除记录文件。
			synchronized (DownLoadThread.class) {
				System.out.println("线程" + i + "下载完毕了");
				--runningThreadCount;
				System.out.println(runningThreadCount);
				if (runningThreadCount < 1) {
					System.out.println("所有的线程都工作完毕了。删除临时记录的文件");
					for (int i = 0; i < threadCount; i++) {
						File f = new File(i + ".txt");
						System.out.println(f.delete());
					}
				}
			}
		}
	}
}

测试结果:

正常情况下的结果如下图



如果在下载的过程中,你突然中断的JVM,下次再次下载的时候,控制台输出的结果如下图:






评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值