多线程断点续传下载

17 篇文章 0 订阅

此方法实现的下载,下载速度达到网速上限十几兆/S。

先讲大体实现思路,再讲其中各种导致下载速度上不去的坑。

 

原理:下载的时候多个线程并发可以占用服务器端更多资源,从而加快下载速度。

 

1.   请求下载链接地址,获取getContentLength,也就是文件总大小。

 

public boolean initDownLoadFileSize() {
		try {
			HttpURLConnection conn = (HttpURLConnection) new URL(downLoadUrl)
                    .openConnection();
			conn.setConnectTimeout(5000);
			conn.setReadTimeout(5000);
			conn.connect();
			int responseCode = conn.getResponseCode();
			if (responseCode == 200) {
                fileSize = conn.getContentLength();
                return true;
            }
		} catch (IOException e) {
		}
		return false;
	}

 

 

 

 

2.  根据文件总大小,决定分几个子线程块去下载。

 

private void convertChunks(){
		int MaximumUserCHUNKS = chunks/2;
		chunks = 1;

		for (int f=1 ; f <=MaximumUserCHUNKS ; f++)
			if (fileSize > MegaByte*f)
				chunks = f*2;

		data = FileDB.getInstance().getFileDownChunkLog(this.package_name);
		if (data.size() == chunks) {
			for (int i = 0; i < chunks; i++) {
				alreadyDownSize += data.get(i + 1);
			}
		} else {
			data.clear();
			for (int i = 0; i < chunks; i++) {
				data.put(i + 1, 0);// 初始化每条线程已经下载的数据长度为0
			}
			FileDB.getInstance().deleteFileDownChunksLog(this.package_name);
			FileDB.getInstance().saveFileDownChunkLog(this.package_name, data);
		}

		workList = new DownloadThread[chunks];

	}

这一步骤核心:使用数据库维护各线程的下载进度,从而实现断点续传的功能。下载之前先读取数据库,查询是否有未完成的记录,有就继续下载,没有则创建新记录插入数据库

 

 

3.    根据分好的块开启子线程开始下载。

 

void startDownloadChunk(){
		InputStream inStream = null;
		RandomAccessFile threadfile = null;
		try {
			HttpURLConnection http = (HttpURLConnection) new URL(downUrl)
					.openConnection();
			http.setConnectTimeout( 10 * 1000 );
			http.setReadTimeout( 10 * 1000 );
			int startPos = block * (threadId - 1) + downLength;
			int endPos;
			if (threadId == downloader.getThreadNum()) {
				endPos = downloader.getFileSize();
			} else {
				endPos = block * threadId - 1;
			}
			http.setRequestProperty("Range", "bytes=" + startPos + "-"
					+ endPos);
			inStream = http.getInputStream();
			byte[] buffer = new byte[1024];
			int offset;
			threadfile = new RandomAccessFile(this.saveFile, "rw");
			threadfile.seek(startPos);
			while ( (offset = inStream.read(buffer)) > 0 ) {
				threadfile.write(buffer, 0, offset);
				downLength += offset;
				downloader.append(offset);

				if (isPause) {
					downloader.Pause();
					return;
				}
			}
			downloader.checkDownloadFinish(this);
		} catch (Exception e) {
			LogUtil.i(e.toString());
			if (tryTime < MAX_RETRY_TIMES) {
				tryTime++;
				startDownloadChunk();
			}else {
				downloader.Pause();
				MyDownloadManager.getInstance().downFail(downloader, "下载失败,请重试",
						true);
			}
		} finally {
			downloader.update(threadId, downLength);
			try {
				if (threadfile != null)
					threadfile.close();
				if (inStream != null)
					inStream.close();
			} catch (IOException e) {
			}
		}
	}

 

 

 

 

 

 

这一块核心主要是两个 

一: 设置http请求的文件长度,http.setRequestProperty("Range", "bytes=" + startPos + "-"     + endPos);

二:使用RandomAccessFile来实现多线程写入同一文件。

 

=======================================分割线=============================================

 

大体实现思路就是如上文所述,是不是感觉没几步,很简单? 0.0 

但其实真正做起来还有很多细节,很多坑在。接下来,作为一个老司机,就谈谈我踩过的一些坑。


1.第一个问题就是分块,到底怎么分,分几个块合适。

    博主最先开始从单线程下载改成多线程下载时,分的子线程都是2个...或者3个.... 改完后对比,差别真的不大....很尴尬,现在想想,当初太小气了。优化后的分块逻辑文章上半部分已贴,16子线程上限。

 

2.第二个问题是RandomAccessFile的创建。

    网上帖子大多数是这么创建的:new RandomAccessFile(file, "rws");  

    rws模式下文件的写入是同步的,不是异步的,会大大降低多线程写入文件的速度哦。

    所以,正确的姿势是这样子的: new RandomAccessFile(file, "rw");  

 

3.第三个问题是各子线程下载进度的保存问题。

    有些同学为了实现断点续传这个功能,跟博主年轻的时候一样,想当然的在写入文件时更新数据库。navie

    频繁读写数据库同样也是降低下载速度的因素。

    优雅的做法是在异常时才去更新保存进度。或者设置一个阀值。

    



 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值