自己写的一个简单的迅雷下载支持断点续传

原创 2015年08月20日 11:08:28

当我学习了网络线程,就自己仿照迅雷下载写了一个下载器,支持断点续传

我用的是SWT插件做的界面 

界面

package com.yc.xunlei;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;

public class Xunlei {

	protected Shell shell;
	private Text txt;

	private Combo combo;
	private long sum;
	private ProgressBar progressBar;

	private File downLoadFile;

	private Text text;

	private Map<String, List<ThreadInfo>> threadInfos = new HashMap<String, List<ThreadInfo>>();

	private Label label_2;

	private String key;

	DownLoadUtils dlu;

	/**
	 * Launch the application.
	 * 
	 * @param args
	 */
	public static void main(String[] args) {
		try {
			Xunlei window = new Xunlei();
			window.open();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * Open the window.
	 */
	public void open() {
		Display display = Display.getDefault();
		createContents();
		shell.open();
		shell.layout();
		while (!shell.isDisposed()) {
			if (!display.readAndDispatch()) {
				display.sleep();
			}
		}
	}

	/**
	 * Create contents of the window.
	 */
	protected void createContents() {
		shell = new Shell();
		shell.setSize(610, 468);
		shell.setText("\u8FC5\u96F7\u4E0B\u8F7D");

		Label lblUrl = new Label(shell, SWT.NONE);
		lblUrl.setBounds(26, 36, 40, 15);
		lblUrl.setText("url:");

		txt = new Text(shell, SWT.BORDER);
		txt
				.setText("http://dlsw.baidu.com/sw-search-sp/soft/3a/12350/QQ_v7.3.15056.0_setup.1435111953.exe");
		txt.setBounds(72, 33, 520, 18);

		Button button = new Button(shell, SWT.NONE);

		button.setBounds(127, 201, 72, 22);
		button.setText("\u4E0B\u8F7D");

		Button button_1 = new Button(shell, SWT.NONE);

		button_1.setBounds(236, 201, 72, 22);
		button_1.setText("\u6682\u505C");

		progressBar = new ProgressBar(shell, SWT.NONE);
		progressBar.setBounds(72, 245, 461, 17);

		Label label = new Label(shell, SWT.NONE);
		label.setBounds(26, 83, 42, 25);
		label.setText("\u7EBF\u7A0B\u6570:");

		combo = new Combo(shell, SWT.NONE);
		combo.setItems(new String[] { "5", "6", "7", "8", "9", "10" });
		combo.setBounds(72, 83, 87, 20);
		combo.select(0);

		Label label_1 = new Label(shell, SWT.NONE);
		label_1.setBounds(26, 141, 54, 18);
		label_1.setText("\u4FDD\u5B58\u4F4D\u7F6E:");

		text = new Text(shell, SWT.BORDER);
		text.setBounds(89, 141, 296, 18);

		text.setText(System.getProperty("user.home"));

		label_2 = new Label(shell, SWT.NONE);
		label_2.setBounds(127, 281, 342, 31);

		// 暂停的方法
		button_1.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				if (dlu != null) {
					dlu.stop();
				}

			}
		});

		/**
		 * a. 创建要下载的文件到本地磁盘 b. 设置界面上progressbar的总长度 c. 再开始下载
		 * d.修改System.out.println("已经下载了:"+ sum+"个字节");为 progressbar的设置
		 */
		button.addSelectionListener(new SelectionAdapter() {
			@Override
			public void widgetSelected(SelectionEvent e) {
				sum = 0; // 每次点ji开始时,要将sum赋初值为0

				String urlString = txt.getText().trim();
				int threadSize = Integer.parseInt(combo.getText());
				String savePath = text.getText();
				try {
					dlu = new DownLoadUtils(threadSize, urlString, savePath);

					downLoadFile = dlu.getDownLoadFile();
					key = threadSize + "_" + urlString + "_"	+ downLoadFile.getAbsolutePath();
					// 设置界面上progressbar的总长度
					progressBar.setMaximum((int) downLoadFile.length());
					long allThreadDownLoadedSize = dlu
							.getAllThreadDownLoadedSize(key);

					label_2.setText("总长度:" + (int) downLoadFile.length()
							+ "/已下载的长度" + allThreadDownLoadedSize);

					sum += allThreadDownLoadedSize;

					 dlu.downLoad(downLoadFile, urlString,
							threadSize, new OnSizeChangeListener() {
								public void onSizeChange(long downLoadSize) {
									sum += downLoadSize;
									Display.getDefault().asyncExec(
											new Runnable() {
												@Override
												public void run() {
													progressBar
															.setSelection((int) sum);
													label_2
															.setText("总长度:"
																	+ (int) downLoadFile
																			.length()
																	+ "/已下载的长度"
																	+ sum);
												}
											});
									if (sum >= downLoadFile.length()) {
										dlu.stop();
										Display.getDefault().asyncExec(
												new Runnable() {
													@Override
													public void run() {
														MessageBox mb = new MessageBox(
																shell, SWT.NO);
														mb.setText("下载完毕");
														mb.setMessage("OK");
														mb.open();
													}
												});

										

									}

								}

							});

				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		});

	}
}

实体类 bean(ThreadInfo)

package com.yc.xunlei;

import java.io.Serializable;

public class ThreadInfo implements Serializable {
	private static final long serialVersionUID = -8664947024042932015L;
	private int threadId;
	private long downLoadSize;

	public int getThreadId() {
		return threadId;
	}

	public void setThreadId(int threadId) {
		this.threadId = threadId;
	}

	public long getDownLoadSize() {
		return downLoadSize;
	}

	public void setDownLoadSize(long downLoadSize) {
		this.downLoadSize = downLoadSize;
	}

	public ThreadInfo(int threadId, long downLoadSize) {
		super();
		this.threadId = threadId;
		this.downLoadSize = downLoadSize;
	}

	public ThreadInfo() {
		super();
	}

	@Override
	public String toString() {
		return threadId + "\t" + downLoadSize;
	}

}

回调接口. 用来通知主线程下载的数据量...

package com.yc.xunlei;

/**
 * 回调接口. 用来通知主线程下载的数据量...
 * @author Administrator
 *
 */
public interface OnSizeChangeListener {
	public void onSizeChange(   long downLoadSize  );
}

下载任务类类

package com.yc.xunlei;

import java.io.File;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;



public class DownLoadTask implements Runnable {
	private File downLoadFile;
	private String urlString;
	private long startPosition;
	private long endPosition;
	private int threadId;
	private OnSizeChangeListener onSizeChangeListener ;
	
	private boolean flag=true;
	
	private long downLoadedSize=0;
	private long downLoadSizePerThread;
	
	
	
	public long getDownLoadedSize() {
		return downLoadedSize;
	}

	public int getThreadId() {
		return threadId;
	}

	public void stop(   ){
		this.flag=false;
		try {
			this.finalize();
		} catch (Throwable e) {
			e.printStackTrace();
		}
	}

	public DownLoadTask(File downLoadFile, String urlString,
			long startPosition, long endPosition, int threadId,   OnSizeChangeListener onSizeChangeListener,  long downLoadSizePerThread ) {
		this.downLoadFile = downLoadFile;
		this.urlString = urlString;
		this.startPosition = startPosition;
		this.endPosition = endPosition;
		this.threadId = threadId;
		this.onSizeChangeListener= onSizeChangeListener ;
		this.downLoadSizePerThread=downLoadSizePerThread;
	}

	public void run() {
		downLoadedSize=     startPosition-   threadId*downLoadSizePerThread;
		try {
			URL url = new URL(urlString);
			HttpURLConnection con = (HttpURLConnection) url.openConnection();
			con.setRequestMethod("GET"); // 请求头
			con.setConnectTimeout(5 * 1000); // 请求过期的时间
			con.setRequestProperty("Connection", "Keep-alive");
			// TODO:发出协议,指定Range
			con.setRequestProperty("Range", "bytes=" + startPosition + "-"
					+ endPosition);
			RandomAccessFile raf = new RandomAccessFile(downLoadFile, "rw");
			// TODO: raf不能从第0个字节写入,而必须从 startPosition位置写入 ,问题来了,如何控制raf从指定位置写入呢?
			raf.seek(startPosition);

			InputStream iis = con.getInputStream();
			byte[] bs = new byte[1024];
			int length = -1;
			while ((length = iis.read(bs, 0, bs.length)) != -1) {
				raf.write(bs, 0, length);
				
				if(  this.onSizeChangeListener!=null  ){
					onSizeChangeListener.onSizeChange(    length   );
				}
				this.downLoadedSize+= length;
				//标量,用于控制线程的暂停
				if(   !flag){
					break;
				}
			}
			iis.close();
			con.disconnect();
			raf.close();
			 System.out.println(threadId + "号线程下载完成,范围" + startPosition + "至"
			 + endPosition);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}
下载 Util类

package com.yc.xunlei;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class DownLoadUtils {
	private List<DownLoadTask> downLoadTasks = new ArrayList<DownLoadTask>();
	private long allThreaddownLoadedSize;
	private String key;
	private Map<String, List<ThreadInfo>> map;
	private int threadSize;
	private String urlString;
	private String savePath;
	private File downLoadFile;
	private List<Thread> threads=new ArrayList<Thread>();

	public File getDownLoadFile() {
		return this.downLoadFile;
	}

	public DownLoadUtils(int threadSize, String urlString, String savePath)
			throws IOException {
		this.threadSize = threadSize;
		this.urlString = urlString;
		this.savePath = savePath;
		key = threadSize + "_" + urlString + "_" + savePath + File.separator
				+ getDownLoadFileName(urlString);
		downLoadFile = createDownLoadFile(urlString, savePath);
		map=getDownLoadedThreadInfoMapFromTmpFile();
		System.out.println("读取到的数据:"+ map );
		if( map==null){
			map=new HashMap< String, List<ThreadInfo>>();
		}
	}

	public long getAllThreadDownLoadedSize(String key) {
		allThreaddownLoadedSize = 0;
		if (map != null && map.size() > 0) {
			List<ThreadInfo> list = map.get(key);
			for (ThreadInfo ti : list) {
				allThreaddownLoadedSize += ti.getDownLoadSize();
			}
		}
		return allThreaddownLoadedSize;
	}

	public void stop() {
		if (downLoadTasks != null && downLoadTasks.size() > 0) {
			for (int i=0;i<threads.size();i++) {
				downLoadTasks.get(i).stop();
				Thread t=threads.get(i);
				t=null;
			}
		}
		if (isDownLoadFinish()) {
			map.remove(key);
		}
		recordDownLoadThreadInfo();
	}

	private void recordDownLoadThreadInfo() {
		ObjectOutputStream oos = null;
		try {
			List<ThreadInfo> list = new ArrayList<ThreadInfo>();
			for (DownLoadTask dlt : downLoadTasks) {
				// 在DownLoadTask中增加一个属性,表示这个线程下载的线据量, 累加
				// 当暂停时,在这里,调用 getxxx方法得到空上线程下载的量.
				// 操作磁盘记录
				ThreadInfo ti = new ThreadInfo();
				ti.setThreadId(dlt.getThreadId());
				ti.setDownLoadSize(dlt.getDownLoadedSize());
				list.add(ti);
			}
			map.put(key, list);
			System.out.println( "保存的数据:"+map );
			FileOutputStream fos = new FileOutputStream(new File(System
					.getProperty("user.home"), "data.tmp"));
			oos = new ObjectOutputStream(fos);
			oos.writeObject(map);
			oos.flush();
		} catch (Exception e1) {
			e1.printStackTrace();
		} finally {
			try {
				if (oos != null) {
					oos.close();
				}
			} catch (IOException e1) {
				e1.printStackTrace();
			}
		}
	}

	private boolean isDownLoadFinish() {
		// 计算当前下载的总长度
		long downSize = 0;
		for (DownLoadTask dlt : downLoadTasks) {
			downSize += dlt.getDownLoadedSize();
		}
		// 文件总长度
		long totalLength = this.downLoadFile.length();
		if (downSize >= totalLength) {
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 多线程下载的实现
	 * 
	 * @param downLoadFile
	 * @param urlString
	 * @param threadSize
	 * @throws IOException
	 */
	public List<DownLoadTask> downLoad(File downLoadFile, String urlString,
			int threadSize, OnSizeChangeListener onSizeChangeListener)
			throws IOException {
		long startPosition = 0; // 当前线程的起始位置
		long endPosition = 0; // 当前线程的结束
		// 获取每个线程要下载的长度
		long downLoadSizePerThread = getDownLoadSizePerThread(downLoadFile
				.length(), threadSize);

		// TODO: 1. 拼接map的键 2. 到 磁盘上找是否有map,map中是否有这个键,
		// 3. 有则取出值 4. 循环来计算这个起始位置
		key = threadSize + "_" + urlString + "_"
				+ downLoadFile.getAbsolutePath();
		Map<String, List<ThreadInfo>> threadInfos = getDownLoadedThreadInfoMapFromTmpFile();
		List<ThreadInfo> list = new ArrayList<ThreadInfo>();
		if (threadInfos != null) {
			list = threadInfos.get(key);
		}
		for (int i = 0; i < threadSize; i++) {
			if (list.size()>0 && list.get(i) != null) {
				// 起始位置
				startPosition = i * downLoadSizePerThread
						+ list.get(i).getDownLoadSize();
				allThreaddownLoadedSize += list.get(i).getDownLoadSize();
			} else {
				// 起始位置
				startPosition = i * downLoadSizePerThread;
			}
			// 终点位置
			endPosition = (i + 1) * downLoadSizePerThread - 1;
			// TODO:这个地方必须取得所有的DownLoadTask的实例, 返回给主界面,再调用
			// DownLoadTask中的某个方法,来设置标量.
			downLoadTasks.add(new DownLoadTask(downLoadFile, urlString,
					startPosition, endPosition, i, onSizeChangeListener,   downLoadSizePerThread ));
		}

		if (allThreaddownLoadedSize < downLoadFile.length()) {
			for (int i = 0; i < threadSize; i++) {
				Thread t=new Thread(downLoadTasks.get(i) );
				threads.add( t );
				t.start();
			}
		}
		return downLoadTasks;
	}

	/**
	 * 读取临时文件中存的已经下载的线程的信息
	 * 
	 * @return
	 */
	private Map<String, List<ThreadInfo>> getDownLoadedThreadInfoMapFromTmpFile() {
		Map<String, List<ThreadInfo>> threadInfos = null;
		ObjectInputStream ois = null;
		FileInputStream fis = null;
		try {
			File f = new File(System.getProperty("user.home"), "data.tmp");
			if (!f.exists()) {
				return null;
			}
			fis = new FileInputStream(f);

			ois = new ObjectInputStream(fis);

			threadInfos = (Map<String, List<ThreadInfo>>) ois.readObject();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				if (ois != null)
					ois.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
			try {
				if (fis != null)
					fis.close();
			} catch (IOException e) {
				e.printStackTrace();
			}

		}
		return threadInfos;
	}

	/**
	 * 计算每个线程要下载的长度
	 * 
	 * @param fileLength
	 * @param threadSize
	 * @return
	 */
	public long getDownLoadSizePerThread(long fileLength, int threadSize) {
		long downLoadSizePerThread = 0;
		downLoadSizePerThread = fileLength % threadSize == 0 ? fileLength
				/ threadSize : fileLength / threadSize + 1;
		return downLoadSizePerThread;
	}

	/**
	 * 将指定的urlString下的文件下载到 savePath路径下
	 * 
	 * @param urlString
	 * @param savePath
	 * @return 保存的文件对象
	 * @throws IOException
	 */
	public File createDownLoadFile(String urlString, String savePath)
			throws IOException {
		// 1. 取出要下载的文件长度,使用 "HEAD"请求头
		long length = getDownLoadFileLength(urlString);
		// 2. 从urlString中取出文件名
		String fileName = getDownLoadFileName(urlString);
		// 3. 创建文件到moren路径或指定路径下
		File downLoadFile = createFile(savePath, fileName, length);
		return downLoadFile;
	}

	/**
	 * 取出要下载的文件的长度
	 * 
	 * @param urlString
	 *            : 要下载的文件的地址
	 * @return length: 文件长度 字节长度
	 * @throws IOException
	 */
	public long getDownLoadFileLength(String urlString) throws IOException {
		long length = -1;
		// 1.取要下载的文件 长度
		URL url = new URL(urlString);
		HttpURLConnection con = (HttpURLConnection) url.openConnection();
		con.setRequestMethod("HEAD"); // 请求头
		con.setConnectTimeout(5 * 1000); // 请求过期的时间
		con.connect();
		length = con.getContentLength();
		return length;
	}

	/**
	 * 根据url获取要下载的文件名
	 * 
	 * @param urlString
	 * @return 要下载的文件名
	 * @throws MalformedURLException
	 * @throws MalformedURLException
	 */
	public String getDownLoadFileName(String urlString)
			throws MalformedURLException {
		if (urlString == null || "".equals(urlString)) {
			throw new IllegalArgumentException("文件名不能为空");
		}
		URL url = new URL(urlString);
		String file = url.getFile();
		String fileName = file.substring(file.lastIndexOf("/") + 1);
		return fileName;
	}

	/**
	 * 根据目录名,文件名,长度,创建一个文件到指定位置
	 * 
	 * @param directory
	 *            : null "" fffff:\\
	 * @param fileName
	 * @param length
	 * @return 创建的文件对象
	 * @throws IOException
	 */
	public File createFile(String directory, String fileName, long length)
			throws IOException {
		String directoryPath = null;
		if (directory != null && !"".equals(directory)
				&& new File(directory).exists()) {
			directoryPath = directory;
		} else {
			directoryPath = System.getProperty("user.home");
		}
		if (fileName == null || "".equals(fileName)) {
			throw new IllegalArgumentException("文件名不存在");
		}
		if (length <= 0) {
			throw new IllegalArgumentException("文件大小不能小于0字节");
		}
		File f = new File(directoryPath, fileName);
		// TODO:要判断这个文件是否存在,没有则创建,有,表示有两种情况: 1. 已经 下载完成, 2. 需要断点续传..
		if (f.exists()) {
			return f;
		}
		RandomAccessFile raf = new RandomAccessFile(f, "rw");
		raf.setLength(length);
		return f;

	}
}

学习了网络线程,自己写一个程序,来加深理解








版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

仿迅雷实现下载工具,支持断点续传和多线程下载以及简单界面展示

这篇博客介绍一下在公司实习时选做的一个项目,虽然实习期间没有全部完成,但觉得这个项目做好的话挺有意思的,就在之后几天赶着完善了这个项目。这个项目是仿照迅雷,实现一个下载工具。 (开发平台: Ubun...

Python写的一个优美的下载器

Python写的一个优美的下载器,很实用的。

类似迅雷下载实现大文件断点续传

不论是网页开发还是客户端程序开发,都有可能遇到文件下载的实现,最简单的办法好像是说使用WebClient.DownLoadFile()实现,但是如果遇到大文件需要做到断点续传,怎么办?我们看看做到断点...

HTTP断点续传(分块传输)

断点续传:指的是在上传/下载时,将任务(一个文件或压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传/下载,如果碰到网络故障,可以从已经上传/下载的部分开始继续上传/下载未完成的部分,而没有...

Java 实现的断点下载

该断点下载可应用于浏览器或者迅雷等下载工具的下载,实现方式有多种多样的,本文只研究了单线程的下载,迅雷等下载工具会自动将下载资源分块并记录每块的起始位置,然后根据系统性能,起多线程下载。 1. 基本...

java后台断点续传 支持html5 video及迅雷工具

最近项目需要用到视频播放,播放文件需要登录验证,及权限控制,所以写了个测试例子。 1)前端界面 您的浏览器不支持HTML5视频 图片如下: ...

Java 服务器端支持断点续传的源代码【支持快车、迅雷】

Java 服务器端支持断点续传的源代码【支持快车、迅雷】(仅支持 HTTP 协议)         网上关于 Java 支持 HTTP 断点续传的文章不少,但关于 Java 服务器端支持 HTTP...

Java 服务器端支持断点续传的源代码【支持快车、迅雷】

网上关于 Java 支持 HTTP 断点续传的文章不少,但关于 Java 服务器端支持 HTTP 断点续传的却比较少。         本文是 Java 服务器端支持 HTTP 断点续传的源代码,支...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)