多线程下载与断点续传

一个多线程下载与断点续传的demo...

 

import java.io.*;
import java.net.*;
import java.util.concurrent.*;

public class Downloader3 {
	URL url;
	File des;
	File cfg;
	long size;
	long sum;
	int taskNum = 10;
	CountDownLatch latch;
	String path = "C:/Documents and Settings/Administrator/桌面/";

	class Task extends Thread {
		int id;
		long start;
		long end;
		boolean error;
		int errTimes;

		public Task(int id, long start, long end) {
			this.id = id;
			this.start = start;
			this.end = end;
		}

		public void run() {
			for (long now = start ; now <= end; ) {
				if (error) {
					if (++errTimes > 5) {
						latch.countDown();
						System.err.format("Task%2d ERROR\n", id);
						return;
					}
					sum -= now - start;
					now = start;
					error = false;
				}
				System.out.format("Task%2d: [%d, %d]\n", id, now, end);
				try {
					HttpURLConnection con = (HttpURLConnection) url.openConnection();
					con.setRequestProperty("RANGE", "bytes=" + now + "-" + end);
					int code = con.getResponseCode();
					if (code / 100 != 2) {
						System.err.format("Task%2d ERROR: %d\n", id, code);
						throw new IOException();
					}
					byte[] buf = new byte[4096];
					InputStream is = con.getInputStream();
					BufferedInputStream bis = new BufferedInputStream(is);
					RandomAccessFile fos = new RandomAccessFile(des, "rw");
					RandomAccessFile raf = new RandomAccessFile(cfg, "rw");
					fos.seek(now);
					for (int len = -1; (len = bis.read(buf)) != -1; ) {
						fos.write(buf, 0, len);
						sum += len;
						now += len;
						raf.seek(8);
						raf.writeLong(sum);
						raf.seek(20 + id * 24 + 16);
						raf.writeLong(now);
						System.out.format("%.2f%%\n", sum * 100.0 / size);
					}
					fos.close();
					raf.close();
					bis.close();
					con.disconnect();
				} catch (IOException e) {
					e.printStackTrace();
					error = true;
					try {
						TimeUnit.SECONDS.sleep(3);
					} catch (InterruptedException e1) {
						e1.printStackTrace();
					}
					continue;
				}
			} // end for
			latch.countDown();
		}
		
	}

	public void download(String address) {
		long begin = System.currentTimeMillis();
		String name = address.substring(address.lastIndexOf('/') + 1);
		des = new File(path + name + ".tmp");
		cfg = new File(path + name + ".cfg");
		latch = new CountDownLatch(taskNum);
		ExecutorService exec = Executors.newCachedThreadPool();
		long[] starts = new long[taskNum];
		long[] ends = new long[taskNum];
		try {
			url = new URL(address);
			analyse(starts, ends);
			for (int i = 0; i < taskNum; i++)
				exec.execute(new Task(i, starts[i], ends[i]));
			latch.await();
			exec.shutdown();
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (sum >= size) {
			des.renameTo(new File(path + name));
			cfg.delete();
		}
		long time = System.currentTimeMillis() - begin;
		System.out.format("Time: %.2fs, Speed: %.2fk/s\n",
							0.001 * time, 0.9765625 * size / time);
	}

	private void analyse(long[] starts, long[] ends) throws IOException {
		RandomAccessFile raf = new RandomAccessFile(cfg, "rw");
		if (!des.exists()) {
			HttpURLConnection con = (HttpURLConnection) url.openConnection();
			size = con.getContentLength();
			long part = size / taskNum;
			raf.writeLong(size);
			raf.writeLong(sum);
			raf.writeInt(taskNum);
			for (int i = 0; i < taskNum; i++) {
				starts[i] = part * i;
				ends[i] = (i == taskNum - 1) ? size - 1 : part * (i + 1) - 1;
				raf.writeLong(starts[i]);
				raf.writeLong(ends[i]);
				raf.writeLong(starts[i]); // now
			}
		} else {
			size = raf.readLong();
			sum = raf.readLong();
			taskNum = raf.readInt();
			for (int i = 0; i < taskNum; i++) {
				raf.seek(20 + i * 24 + 8);
				ends[i] = raf.readLong();
				starts[i] = raf.readLong();
			}
		}
		raf.close();
	}

	public static void main(String[] args) {
		Downloader3 d = new Downloader3();
		String address = "http://cidian.youdao.com/download/YoudaoDict.exe";
		d.download(address);
	}

}

  

总结:

1. "RANGE"属于闭区间类型, 对右端点值不好理解可以通过干脆不写来变通, 如果区间不合理会导致下载文件空洞...

2. 每个下载子线程对主文件和配置文件的访问要定义局部的RandomAccessFile, 否则会有线程错误...

3. 关闭配置文件的全部RandomAccessFile流, 否则会导致下载完成时删除配置文件失败...

4. 程序处理异常部分极度ugly, 怎么改也是难看...

5. 已用下载时间, 平均下载速度统计信息用专门的线程来做统计比较好...

6. 没实现各种运行参数的灵活设置...

7. 非首次运行时线程调度部分实现的不合理, 难以实现高效下载...

8. 对网络, 线程, IO的API远不够熟悉, 难以实现灵活合理的运用. 也许使用NIO性能会好点儿?

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值