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

我用的是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() {@Overridepublic void widgetSelected(SelectionEvent e) {if (dlu != null) {dlu.stop();}}});/*** a. 创建要下载的文件到本地磁盘 b. 设置界面上progressbar的总长度 c. 再开始下载* d.修改System.out.println("已经下载了:"+ sum+"个字节");为 progressbar的设置*/button.addSelectionListener(new SelectionAdapter() {@Overridepublic void widgetSelected(SelectionEvent e) {sum = 0; // 每次点ji开始时,要将sum赋初值为0String 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() {@Overridepublic void run() {progressBar.setSelection((int) sum);label_2.setText("总长度:"+ (int) downLoadFile.length()+ "/已下载的长度"+ sum);}});if (sum >= downLoadFile.length()) {dlu.stop();Display.getDefault().asyncExec(new Runnable() {@Overridepublic 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();}@Overridepublic 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:发出协议,指定Rangecon.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;}
}

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

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

  1. 用shell脚本写的一个简单的俄罗斯方块

    用shell脚本写的一个简单的俄罗斯方块 代码 代码 测试 下载链接 代码 代码 #!/bin/bash #version 1.2,若非正常退出,请使用附带的killel.sh脚本杀死进程 #定义用于 ...

  2. 自己写的一个简单的android记事本app

    自己写的一个简单的记事本app,效果如下: 一.首先是第一个界面的编写,最上面是一个TextView,中间是一个Linearlayout中嵌套一个listview布局,最下面是一个button.下面附 ...

  3. easy-mock写的一个简单的模拟二页的反馈

    用easy-mock写的一个简单的模拟二页的反馈,因为后端团队比较传统,所以设计的结构不太规范. 功能:每页10条,共2页,共12条记录,超出参数范围会返错误码: easy模板: {code: fun ...

  4. AndroidSDK开发6我用kotlin协程写了一个简单sdk

    目录 AndroidSDK开发6我用kotlin协程写了一个简单sdk 1.kotlin的依赖和导包如下:(//如果不使用协程可以去掉协程的导包减少sdk包大小) 2.Application代码如下: ...

  5. 最近写了一个简单的面向对象的脚本语言 Q 语言

    最近写了一个简单的面向对象的脚本语言 Q 语言,语法类似于 Javascript, 加入了一些 python 的语法功能. 同时实现了部分的 Javascript prototype 的功能 (个人觉 ...

  6. 用li写的一个简单的横向导航菜单demo

    用ul li写的一个简单的横向导航菜单,很实用: /* 所有class为menu的div中的ul样式 */ div.menu ul { list-style:none; /* 去掉ul前面的符号 */ ...

  7. 【教程】Chrome浏览器添加迅雷下载支持

    今天为了尝试一下Ubuntu操作系统,百度搜索了一下,最后在异次元软件网找到了相关的下载资源,那作为一个小白,肯定是选择装一个虚拟机,再装个双系统来学习一下的啦.一开始因为我的电脑没有安装迅雷,选择了 ...

  8. python迅雷远程下载页面_【教程】Chrome浏览器添加迅雷下载支持

    2019年4月28日更新: 目前官方最新版本的迅雷X已经实现自动安装Chrome扩展,无需手动安装. 请仍在使用老版本迅雷的雷友升级到官方最新版本的迅雷X. 以下内容已经过期 最近一段时间,Googl ...

  9. Android实现网络下载一(单任务下载--支持断点续传)

    Android实现网络下载一(单任务下载–支持断点续传) 说起下载,在Android的一些类似游戏宝的项目中会频繁使用,比如说需求要下载一个apk文件,并且要支持暂停.继续等要求.当然在GitHib上 ...

最新文章

  1. mysql创建表语句 引擎、编码和字符集设置
  2. ROS Master IP
  3. swift python javascript_最小的Swift App
  4. Kafka学习笔记之Kafka三款监控工具
  5. findfont: Font family [‘sans-serif‘] not found. Falling back to DejaVu Sans.
  6. 基于Java JAAS表单的身份验证
  7. mongodb下载安装和基本操作
  8. 《UniBench A Benchmark for Multi-Model Database Management Systems》阅读笔记
  9. 老湿人----山河拱手,为君一笑
  10. 二维码制作生成器有哪些?分享几个二维码制作生成器
  11. 【Java数据结构与算法】Java如何实现环形队列
  12. java空气质量指数AQI换算
  13. 融云 x OHLA:「社交+游戏」双轮驱动,逐鹿中东陌生人社交
  14. Android-再次解读萤石云视频
  15. 体验共享单车后对于Locman技术实现的几点思考
  16. ios 开发设置左滑退出_iOS 开发UITableView左滑出现删除按钮的运用方法
  17. 关于视觉SLAM的最先进技术的调查-A survey of state-of-the-art on visual SLAM
  18. python 群发邮件数量限制_python群发邮件
  19. 读《多头自注意力层和卷积层的关系》笔记
  20. 46个不得不知的生活小常识

热门文章

  1. 数字谜1 C实现
  2. java编程题学习与心得
  3. miui相机位置服务器,如何开启“MIUI相机”实验室功能
  4. 关于计算机教育的感想
  5. dnsmasq no body user or group
  6. springcontext.xml 中方言是红色的_影评:陆河方言电影我的村,我的家值得一看
  7. 子比主题美化插件-PPHU美化插件
  8. Nextracker上市:市值45亿美元 为美股今年最大IPO
  9. 聊聊苹果的Bug - iOS 10 nano_free Crash.md
  10. 别忙,我先感觉一下自己