多线程断点续传

package mydown;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * 多线程断点续传
 * 
 * @author kinkding
 * @history 2009-7-25
 */
public class TestMyDown {

	int threads;
	String rootPath = "F:/mydown";
	String fileName = "";
	URL downUrl;
	CountDownLatch latch;

	public TestMyDown(String strUrl, int threads) throws MalformedURLException {
		downUrl = new URL(strUrl);
		this.threads = threads;
		fileName = strUrl.substring(strUrl.lastIndexOf('/') + 1);
		latch = new CountDownLatch(threads);
	}

	public int run() throws IOException {
		// 创建根路径
		File rootFile = new File(rootPath);
		if (!rootFile.exists()) {
			rootFile.mkdirs();
		}

		HttpURLConnection urlConn = (HttpURLConnection) downUrl.openConnection();
		this.setHeader(urlConn);
		long totalLength = urlConn.getContentLength();
		long threadLength = totalLength / threads;
		// 设置断点
		long startPos[] = this.setBreakPoint(totalLength);
		// 开始进行多线程下载
		DownThread downThreads[] = new DownThread[threads];
		ExecutorService exec = Executors.newCachedThreadPool();
		for (int i = 0; i < threads; i++) {
			startPos[i] += threadLength * i;
			long endPos = i == threads - 1 ? totalLength : (threadLength * (i + 1) - 1);
			// 开启子线程并执行
			DownThread child = new DownThread(startPos[i], endPos, i);
			downThreads[i] = child;
			exec.execute(child);
		}
		try {
			// 等待所有线程下载完成
			latch.await();
			exec.shutdown();
			// 开始合并文件
			this.mergeFile(downThreads);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		return 1;
	}

	private long[] setBreakPoint(long totalLength) throws IOException {
		long startPos[] = new long[threads];
		File file = new File(this.rootPath + '/' + this.fileName);
		if (file.exists() && file.length() < totalLength) {
			File root = new File(this.rootPath);
			for (File f : root.listFiles()) {
				String name = f.getName();
				long length = f.length();
				if (name != null && length > 0 && name.startsWith(this.fileName + '_')) {
					startPos[Integer.parseInt(name.substring(name.lastIndexOf('_') + 1))] = length;
				}
			}
		} else {
			file.createNewFile();
		}
		return startPos;
	}

	private void mergeFile(DownThread downThreads[]) throws IOException {
		BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(this.rootPath
				+ "/" + this.fileName));
		for (int i = 0; i < threads; i++) {
			BufferedInputStream input = new BufferedInputStream(new FileInputStream(
					downThreads[i].file));
			int len = 0;
			long count = 0;
			byte[] b = new byte[1024];
			while ((len = input.read(b)) != -1) {
				count += len;
				output.write(b, 0, len);
				if ((count % 4096) == 0) {
					output.flush();
				}
			}
			input.close();
			downThreads[i].file.delete();
		}
		output.flush();
		output.close();
		System.out.println(fileName + " ok!");
	}

	private void setHeader(HttpURLConnection urlConn) {
		// 模拟浏览器的访问
		urlConn
				.setRequestProperty("User-Agent",
						"Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.1.1) Gecko/20090715 Firefox/3.5.1");
		urlConn.setRequestProperty("Accept-Language", "zh-cn,zh;q=0.5");
		urlConn.setRequestProperty("Accept-Charset", "GB2312,utf-8;q=0.7,*;q=0.7");
		urlConn.setRequestProperty("Keep-Alive", "300");
		urlConn.setRequestProperty("Connection", "keep-alive");
		urlConn.setRequestProperty("Cache-Control", "max-age=0");
		urlConn.setRequestProperty("Referer", downUrl.getPath());
	}

	class DownThread extends Thread {
		private File file;
		private int id;
		private long startPos;
		private long endPos;

		public DownThread(long startPos, long endPos, int id) {
			super();
			this.startPos = startPos;
			this.endPos = endPos;
			this.id = id;
			file = new File(rootPath + "/" + fileName + "_" + id);
			if (!file.exists()) {
				try {
					file.createNewFile();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}

		public void run() {
			System.out.println("thread " + id + " start...");
			HttpURLConnection con = null;
			InputStream inputStream = null;
			BufferedOutputStream outputStream = null;
			long count = 0;
			try {
				outputStream = new BufferedOutputStream(new FileOutputStream(file.getPath(), true));
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			}
			while (true) {
				try {
					con = (HttpURLConnection) downUrl.openConnection();
					setHeader(con);
					con.setAllowUserInteraction(true);
					con.setConnectTimeout(1000);
					con.setReadTimeout(1000);

					if (startPos < endPos) {
						// 设置下载数据的起止区间
						System.out.println("thread" + id + " bytes=" + startPos + "-" + endPos);
						con.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);
						int code = con.getResponseCode();
						// 服务器是否响应成功
						if (code != HttpURLConnection.HTTP_OK
								&& code != HttpURLConnection.HTTP_PARTIAL) {
							outputStream.close();
							con.disconnect();
							latch.countDown();
							break;
						}

						inputStream = con.getInputStream();
						int len = 0;
						byte[] b = new byte[1024];
						while ((len = inputStream.read(b)) != -1) {
							outputStream.write(b, 0, len);
							count += len;
							startPos += len;
							if (count % 4096 == 0) {
								outputStream.flush();
							}
						}
						outputStream.flush();
						outputStream.close();
						inputStream.close();
						con.disconnect();
					}
					System.out.println("thread " + id + " end.");
					latch.countDown();
					break;
				} catch (IOException e) {
					try {
						outputStream.flush();
						inputStream.close();
						con.disconnect();
						// 2秒后重连
						TimeUnit.SECONDS.sleep(1);
					} catch (InterruptedException e1) {
						e1.printStackTrace();
					} catch (IOException e2) {
						e2.printStackTrace();
					}
					continue;
				}
			}
			// 循环终止
			if (outputStream != null) {
				try {
					outputStream.close();
					con.disconnect();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	public static void main(String[] args) {
		String url = "http://i1.sinaimg.cn/dy/hdphoto/2009/0425/U1565P1T557D132F12892DT20090425105719.jpg";
		try {
			TestMyDown mydown = new TestMyDown(url, 4);
			mydown.run();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}

 

程序运行结果如下:

thread 0 start...
thread0 bytes=0-46078
thread 1 start...
thread1 bytes=46079-92157
thread 2 start...
thread2 bytes=92158-138236
thread 3 start...
thread3 bytes=138237-184319
thread2 bytes=92158-138236
thread 0 end.
thread1 bytes=74995-92157
thread 1 end.
thread3 bytes=174453-184319
thread 3 end.
thread2 bytes=114648-138236
thread 2 end.
U1565P1T557D132F12892DT20090425105719.jpg ok!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。 经导师精心指导并认可、获 98 分的毕业设计项目!【项目资源】:微信小程序。【项目说明】:聚焦计算机相关专业毕设及实战操练,可作课程设计与期末大作业,含全部源码,能直用于毕设,经严格调试,运行有保障!【项目服务】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值