- 浏览: 409262 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (347)
- java基础 (58)
- ajax (10)
- s2sh (10)
- 版本控制 (4)
- 数据库 (34)
- 服务器 (4)
- 开发工具 (8)
- javascript (15)
- soockte (5)
- ext (2)
- 环境搭建 (7)
- struts2 (9)
- 找工作中的面试技巧 (2)
- 承接网站零活 (0)
- JNI+JONSE+OGNL (8)
- 性能优化 (4)
- Android开发 (5)
- xul (8)
- jquery (2)
- 线程 (3)
- jsp+jdbc (7)
- servlet (2)
- java对xml操作 (1)
- IO流的操作 (10)
- 项目开发前配置 (1)
- css (0)
- 上传、下载 (2)
- 知识探讨 (2)
- html (2)
- HQL (0)
- 工作技巧 (1)
- IT (1)
- Hibernate杂谈 (10)
- Spring杂谈 (35)
- DWR (5)
- JUnit测试 (3)
- EasyMock测试web (1)
- ibatis (6)
- maysql (5)
- C++ (0)
- 正则表达式(解剖) (1)
- 密码安全 (2)
- 上传 (1)
- socket (1)
- jni(java与c++结合) (1)
- jdk版本问题 (0)
- tomcat版本问题 (5)
- linux基本命令(初学) (7)
- linux项目发布 (1)
- 3年的经验总结 (1)
- 加解密 (2)
- 高级java阶段 (2)
- java内存分区 (1)
- 浏览器 (1)
- 职业规划 (1)
- 管理 (5)
- java语音 (1)
- SSH (1)
- jsp (3)
- extjs (1)
- uml (2)
- 加密 (1)
- web (2)
- Ant (1)
- 自述 (1)
- Linux (1)
- ssh源码解剖 (1)
- 代码优化 (1)
- 设计模式 (0)
- xml (2)
- JOSN (1)
- scala (0)
- hadoop (0)
- spark (0)
- hana (1)
- shior (1)
- java Word (6)
- java PDF (4)
- java Excel (0)
最新评论
-
高级java工程师:
ztao2333 写道谢谢。收藏下这个总结。呵呵
温习jdk和tomcat -
ztao2333:
大写的,不是大学的
温习jdk和tomcat -
ztao2333:
谢谢。收藏下这个总结。
温习jdk和tomcat -
the_small_base_:
你好,可以提供调用方法吗?需要的Jar,能发下源码吗?谢谢
java实现语音 -
高级java工程师:
文思涌动 写道楼主新年好。可否再传一遍给我,我没有收到, 不清 ...
s2sh整合
关键字: java http 下载 多线程
下载工具我想没有几个人不会用的吧,前段时间比较无聊,花了点时间用java写了个简单的http多线程下载程序,纯粹是无聊才写的,只实现了几个简单的功能,而且也没写界面,今天正好也是一个无聊日,就拿来写篇文章,班门弄斧一下,觉得好给个掌声,不好也不要喷,谢谢!
我实现的这个http下载工具功能很简单,就是一个多线程以及一个断点恢复,当然下载是必不可少的。那么大概先整理一下要做的事情:
1、 连接资源服务器,获取资源信息,创建文件
2、 切分资源,多线程下载
3、 断点恢复功能
4、 下载速率统计
大概就这几点吧,那么首先要做的就是连接资源并获取资源信息,我这里使用了JavaSE自带的URLConnection进行资源连接,大致代码如下:
java代码:
1.String urlStr = “http://www.sourcelink.com/download/xxx”; //资源地址,随便写的
2.URL url = new URL(urlStr); //创建URL
3.URLConnection con = url.openConnection(); //建立连接
4.contentLen = con.getContentLength(); //获得资源长度
5.File file = new File(filename); //根据filename创建一个下载文件,也会是我们最终下载所得的文件
很简单吧,没错就是这么简单,第一步做完了,那么接下来要做第二步,切分资源,实现多线程。在上一步我们已经获得了资源的长度contentLen,那么如何根据这个对资源进行切分呢?假如我们要运行十个线程,那么我们就先把contentLen处以10,获得每块的大小,然后在分别创建十个线程,每个线程负责其中一块的写入,这就需要利用到RandomAccessFile这个类了,这个类提供了对文件的随机访问,可以指定向文件中的某一个位置进行写入操作,大致代码如下:
java代码:
1.long subLen = contentLen / threadQut; //获取每块的大小
2.
3.//创建十个线程,并启动线程
4.for (int i = 0; i < threadQut; i++) {
5. DLThread thread = new DLThread(this, i + 1, subLen * i, subLen * (i + 1) - 1); //创建线程
6. dlThreads[i] = thread;
7. QSEngine.pool.execute(dlThreads[i]); //把线程交给线程池进行管理
8.}
long subLen = contentLen / threadQut; //获取每块的大小
//创建十个线程,并启动线程
for (int i = 0; i < threadQut; i++) {
DLThread thread = new DLThread(this, i + 1, subLen * i, subLen * (i + 1) - 1); //创建线程
dlThreads[i] = thread;
QSEngine.pool.execute(dlThreads[i]); //把线程交给线程池进行管理
}
在这里使用到了DLThread这个类,我们先来看看这个类的构造方法的定义:
public DLThread(DLTask dlTask, int id, long startPos, long endPos)
第一个参数为一个DLTask,这个类就代表一个下载任务,里面主要保存这一个下载任务的信息,包括下载资源名,本地文件名等等的信息。第二个参数就是一个标示线程的id,如果有10个线程,那么这个id就是从1到10,第三个参数startPos代表该线程从文件的哪个地方开始写入,最后一个参数endPos代表写到哪里就结束。
我们再来看看,一个线程启动后,具体如何去下载,请看run方法:
java代码:
1.public void run() {
2. System.out.println("线程" + id + "启动......");
3. BufferedInputStream bis = null; //创建一个buff
4. RandomAccessFile fos = null;
5. byte[] buf = new byte[BUFFER_SIZE]; //缓冲区大小
6. URLConnection con = null;
7. try {
8. con = url.openConnection(); //创建连接,这里会为每个线程都创建一个连接
9. con.setAllowUserInteraction(true);
10. if (isNewThread) {
11. con.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);//设置获取资源数据的范围,从startPos到endPos
12. fos = new RandomAccessFile(file, "rw"); //创建RandomAccessFile
13. fos.seek(startPos); //从startPos开始
14. } else {
15. con.setRequestProperty("Range", "bytes=" + curPos + "-" + endPos);
16. fos = new RandomAccessFile(dlTask.getFile(), "rw");
17. fos.seek(curPos);
18. }
19. //下面一段向根据文件写入数据,curPos为当前写入的未知,这里会判断是否小于endPos,
20. //如果超过endPos就代表该线程已经执行完毕
21. bis = new BufferedInputStream(con.getInputStream());
22. while (curPos < endPos) {
23. int len = bis.read(buf, 0, BUFFER_SIZE);
24. if (len == -1) {
25. break;
26. }
27. fos.write(buf, 0, len);
28. curPos = curPos + len;
29. if (curPos > endPos) {
30. readByte += len - (curPos - endPos) + 1; //获取正确读取的字节数
31. } else {
32. readByte += len;
33. }
34. }
35. System.out.println("线程" + id + "已经下载完毕。");
36. this.finished = true;
37. bis.close();
38. fos.close();
39. } catch (IOException ex) {
40. ex.printStackTrace();
41. throw new RuntimeException(ex);
42. }
43.}
public void run() {
System.out.println("线程" + id + "启动......");
BufferedInputStream bis = null; //创建一个buff
RandomAccessFile fos = null;
byte[] buf = new byte[BUFFER_SIZE]; //缓冲区大小
URLConnection con = null;
try {
con = url.openConnection(); //创建连接,这里会为每个线程都创建一个连接
con.setAllowUserInteraction(true);
if (isNewThread) {
con.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);//设置获取资源数据的范围,从startPos到endPos
fos = new RandomAccessFile(file, "rw"); //创建RandomAccessFile
fos.seek(startPos); //从startPos开始
} else {
con.setRequestProperty("Range", "bytes=" + curPos + "-" + endPos);
fos = new RandomAccessFile(dlTask.getFile(), "rw");
fos.seek(curPos);
}
//下面一段向根据文件写入数据,curPos为当前写入的未知,这里会判断是否小于endPos,
//如果超过endPos就代表该线程已经执行完毕
bis = new BufferedInputStream(con.getInputStream());
while (curPos < endPos) {
int len = bis.read(buf, 0, BUFFER_SIZE);
if (len == -1) {
break;
}
fos.write(buf, 0, len);
curPos = curPos + len;
if (curPos > endPos) {
readByte += len - (curPos - endPos) + 1; //获取正确读取的字节数
} else {
readByte += len;
}
}
System.out.println("线程" + id + "已经下载完毕。");
this.finished = true;
bis.close();
fos.close();
} catch (IOException ex) {
ex.printStackTrace();
throw new RuntimeException(ex);
}
}
上面的代码就是根据startPos和endPos对文件机型写操作,每个线程都有自己独立的一个资源块,从startPos到endPos。上面的方式就是线程下载的核心,多线程搞定后,接下来就是实现断点恢复的功能,其实断点恢复无非就是记录下每个线程完成到哪个未知,在这里我就是使用curPos进行的记录,大家在上面的代码就应该可以看到,我会记录下每个线程的curPos,然后在线程重新启动的时候,就把curPos当成是startPos,而endPost则不变即可,大家有没注意到run方法里有一段这样的代码:
java代码:
1.if (isNewThread) { //判断是否断点,如果true,代表是一个新的下载线程,而不是断点恢复
2. con.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);//设置获取资源数据的范围,从startPos到endPos
3. fos = new RandomAccessFile(file, "rw"); //创建RandomAccessFile
4. fos.seek(startPos); //从startPos开始
5.} else {
6. con.setRequestProperty("Range", "bytes=" + curPos + "-" + endPos);//使用curPos替代startPos,其他都和新创建一个是一样的。
7. fos = new RandomAccessFile(dlTask.getFile(), "rw");
8. fos.seek(curPos);
9.}
上面就是断点恢复的做法了,和新创建一个线程没什么不同,只是startPos不一样罢了,其他都一样,不过仅仅有这个还不够,因为如果程序关闭的话,这些信息又是如何保存呢?例如文件名啊,每个线程的curPos啊等等,大家在使用下载软件的时候,相信都会发现在软件没下载完的时候,在目录下会有两个临时文件,而其中一个就是用来保存下载任务的信息的,如果没有这些信息,程序是不知道该如何恢复下载进度的。而我这里又如何实现的呢?我这个人比较懒,又不想再创建一个文件来保存信息,然后自己又要读取信息创建对象,那太麻烦了,所以我想到了java提供序列化机制,我的想法就是直接把整个DLTask的对象序列化到硬盘上,上面说过DLTask这个类就是用来保存每个任务的信息的,所以我只要在需要恢复的时候,反序列化这个对象,就可以很容易的实现了断点功能,我们来看看这个对象保存的信息:
java代码:
1.public class DLTask extends Thread implements Serializable {
2.
3. private static final long serialVersionUID = 126148287461276024L;
4. private final static int MAX_DLTHREAD_QUT = 10; //最大下载线程数量
5. /**
6. * 下载临时文件后缀,下载完成后将自动被删除
7. */
8. public final static String FILE_POSTFIX = ".tmp";
9. private URL url;
10. private File file;
11. private String filename;
12. private int id;
13. private int Level;
14. private int threadQut; //下载线程数量,用户可定制
15. private int contentLen; //下载文件长度
16. private long completedTot; //当前下载完成总数
17. private int costTime; //下载时间计数,记录下载耗费的时间
18. private String curPercent; //下载百分比
19. private boolean isNewTask; //是否新建下载任务,可能是断点续传任务
20.
21. private DLThread[] dlThreads; //保存当前任务的线程
22.
23.transient private DLListener listener; //当前任务的监听器,用于即时获取相关下载信息
如上代码,这个对象实现了Serializable接口,保存了任务的所有信息,还包括有每个线程对象dlThreads,这样子就可以很容易做到断点的恢复了,让我重新写一个文件保存这些信息,然后在恢复的时候再根据这些信息创建一个对象,那简直是要我的命。这里创建了一个方法,用于断点恢复用:
1.private void resumeTask() {
2. listener = new DLListener(this);
3. file = new File(filename);
4. for (int i = 0; i < threadQut; i++) {
5. dlThreads[i].setDlTask(this);
6. QSEngine.pool.execute(dlThreads[i]);
7. }
8. QSEngine.pool.execute(listener);
9.}
实际上就是减少了先连接资源,然后进行切分资源的代码,因为这些信息已经都被保存在DLTask的对象下了。
看到上面的代码,不知道大家注意到有一个对象DLListener没有,这个对象实际上就是用于监听整个任务的信息的,这里我主要用于两个目的,一个是定时的对DLTask进行序列化,保存任务信息,用于断点恢复,一个就是进行下载速率的统计,平均多长时间进行一个统计。我们先来看下它的代码,这个类也是一个单独的线程:
java代码:
1.public void run() {
2.
3. int i = 0;
4. BigDecimal completeTot = null; //完成的百分比
5. long start = System.currentTimeMillis(); //当前时间,用于记录开始统计时间
6. long end = start;
7.
8. while (!dlTask.isComplete()) { //整个任务是否完成,没有完成则继续循环
9. i++;
10. String percent = dlTask.getCurPercent(); //获取当前的完成百分数
11.
12. completeTot = new BigDecimal(dlTask.getCompletedTot()); //获取当前完成的总字节数
13.
14. //获得当前时间,然后与start时间比较,如果不一样,利用当前完成的总数除以所使用的时间,获得一个平均下载速度
15. end = System.currentTimeMillis();
16. if (end - start != 0) {
17. BigDecimal pos = new BigDecimal(((end - start) / 1000) * 1024);
18. System.out.println("Speed :"
19. + completeTot
20. .divide(pos, 0, BigDecimal.ROUND_HALF_EVEN)
21. + "k/s " + percent + "% completed. ");
22. }
23. recoder.record(); //将任务信息记录到硬盘
24. try {
25. sleep(3000);
26. } catch (InterruptedException ex) {
27. ex.printStackTrace();
28. throw new RuntimeException(ex);
29. }
30.
31. }
32. //以下是下载完成后打印整个下载任务的信息
33. int costTime =+ (int)((System.currentTimeMillis() - start) / 1000);
34. dlTask.setCostTime(costTime);
35. String time = QSDownUtils.changeSecToHMS(costTime);
36.
37. dlTask.getFile().renameTo(new File(dlTask.getFilename()));
38. System.out.println("Download finished. " + time);
39.}
这个方法中的recoder.record()方法的调用就是用于序列化任务对象,其他的代码均为统计信息用的,具体可看注释,record该方法的代码如下:
java代码:
1.public void record() {
2. ObjectOutputStream out = null;
3. try {
4. out = new ObjectOutputStream(new FileOutputStream(dlTask.getFilename() + ".tsk"));
5. out.writeObject(dlTask);
6. out.close();
7. } catch (IOException ex) {
8. ex.printStackTrace();
9. throw new RuntimeException(ex);
10. } finally {
11. try {
12. out.close();
13. } catch (IOException ex) {
14. ex.printStackTrace();
15. throw new RuntimeException(ex);
16. }
17. }
18.
19.}
到这里,大致的代码都完成了,不过以上的代码都是部分片段,只是作为一个参考给大家看下,而且由于本人水平有限,代码很多地方都没有经过过多的考虑,没有经过优化,仅仅只是自娱自乐,所以可能有很多地方都写的很烂,这个程序也缺乏很多功能,连界面都没有,所以整个程序的代码就不上传了,免得丢人,呵呵。希望对有兴趣的朋友尽到一点帮助吧。
下载工具我想没有几个人不会用的吧,前段时间比较无聊,花了点时间用java写了个简单的http多线程下载程序,纯粹是无聊才写的,只实现了几个简单的功能,而且也没写界面,今天正好也是一个无聊日,就拿来写篇文章,班门弄斧一下,觉得好给个掌声,不好也不要喷,谢谢!
我实现的这个http下载工具功能很简单,就是一个多线程以及一个断点恢复,当然下载是必不可少的。那么大概先整理一下要做的事情:
1、 连接资源服务器,获取资源信息,创建文件
2、 切分资源,多线程下载
3、 断点恢复功能
4、 下载速率统计
大概就这几点吧,那么首先要做的就是连接资源并获取资源信息,我这里使用了JavaSE自带的URLConnection进行资源连接,大致代码如下:
java代码:
1.String urlStr = “http://www.sourcelink.com/download/xxx”; //资源地址,随便写的
2.URL url = new URL(urlStr); //创建URL
3.URLConnection con = url.openConnection(); //建立连接
4.contentLen = con.getContentLength(); //获得资源长度
5.File file = new File(filename); //根据filename创建一个下载文件,也会是我们最终下载所得的文件
很简单吧,没错就是这么简单,第一步做完了,那么接下来要做第二步,切分资源,实现多线程。在上一步我们已经获得了资源的长度contentLen,那么如何根据这个对资源进行切分呢?假如我们要运行十个线程,那么我们就先把contentLen处以10,获得每块的大小,然后在分别创建十个线程,每个线程负责其中一块的写入,这就需要利用到RandomAccessFile这个类了,这个类提供了对文件的随机访问,可以指定向文件中的某一个位置进行写入操作,大致代码如下:
java代码:
1.long subLen = contentLen / threadQut; //获取每块的大小
2.
3.//创建十个线程,并启动线程
4.for (int i = 0; i < threadQut; i++) {
5. DLThread thread = new DLThread(this, i + 1, subLen * i, subLen * (i + 1) - 1); //创建线程
6. dlThreads[i] = thread;
7. QSEngine.pool.execute(dlThreads[i]); //把线程交给线程池进行管理
8.}
long subLen = contentLen / threadQut; //获取每块的大小
//创建十个线程,并启动线程
for (int i = 0; i < threadQut; i++) {
DLThread thread = new DLThread(this, i + 1, subLen * i, subLen * (i + 1) - 1); //创建线程
dlThreads[i] = thread;
QSEngine.pool.execute(dlThreads[i]); //把线程交给线程池进行管理
}
在这里使用到了DLThread这个类,我们先来看看这个类的构造方法的定义:
public DLThread(DLTask dlTask, int id, long startPos, long endPos)
第一个参数为一个DLTask,这个类就代表一个下载任务,里面主要保存这一个下载任务的信息,包括下载资源名,本地文件名等等的信息。第二个参数就是一个标示线程的id,如果有10个线程,那么这个id就是从1到10,第三个参数startPos代表该线程从文件的哪个地方开始写入,最后一个参数endPos代表写到哪里就结束。
我们再来看看,一个线程启动后,具体如何去下载,请看run方法:
java代码:
1.public void run() {
2. System.out.println("线程" + id + "启动......");
3. BufferedInputStream bis = null; //创建一个buff
4. RandomAccessFile fos = null;
5. byte[] buf = new byte[BUFFER_SIZE]; //缓冲区大小
6. URLConnection con = null;
7. try {
8. con = url.openConnection(); //创建连接,这里会为每个线程都创建一个连接
9. con.setAllowUserInteraction(true);
10. if (isNewThread) {
11. con.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);//设置获取资源数据的范围,从startPos到endPos
12. fos = new RandomAccessFile(file, "rw"); //创建RandomAccessFile
13. fos.seek(startPos); //从startPos开始
14. } else {
15. con.setRequestProperty("Range", "bytes=" + curPos + "-" + endPos);
16. fos = new RandomAccessFile(dlTask.getFile(), "rw");
17. fos.seek(curPos);
18. }
19. //下面一段向根据文件写入数据,curPos为当前写入的未知,这里会判断是否小于endPos,
20. //如果超过endPos就代表该线程已经执行完毕
21. bis = new BufferedInputStream(con.getInputStream());
22. while (curPos < endPos) {
23. int len = bis.read(buf, 0, BUFFER_SIZE);
24. if (len == -1) {
25. break;
26. }
27. fos.write(buf, 0, len);
28. curPos = curPos + len;
29. if (curPos > endPos) {
30. readByte += len - (curPos - endPos) + 1; //获取正确读取的字节数
31. } else {
32. readByte += len;
33. }
34. }
35. System.out.println("线程" + id + "已经下载完毕。");
36. this.finished = true;
37. bis.close();
38. fos.close();
39. } catch (IOException ex) {
40. ex.printStackTrace();
41. throw new RuntimeException(ex);
42. }
43.}
public void run() {
System.out.println("线程" + id + "启动......");
BufferedInputStream bis = null; //创建一个buff
RandomAccessFile fos = null;
byte[] buf = new byte[BUFFER_SIZE]; //缓冲区大小
URLConnection con = null;
try {
con = url.openConnection(); //创建连接,这里会为每个线程都创建一个连接
con.setAllowUserInteraction(true);
if (isNewThread) {
con.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);//设置获取资源数据的范围,从startPos到endPos
fos = new RandomAccessFile(file, "rw"); //创建RandomAccessFile
fos.seek(startPos); //从startPos开始
} else {
con.setRequestProperty("Range", "bytes=" + curPos + "-" + endPos);
fos = new RandomAccessFile(dlTask.getFile(), "rw");
fos.seek(curPos);
}
//下面一段向根据文件写入数据,curPos为当前写入的未知,这里会判断是否小于endPos,
//如果超过endPos就代表该线程已经执行完毕
bis = new BufferedInputStream(con.getInputStream());
while (curPos < endPos) {
int len = bis.read(buf, 0, BUFFER_SIZE);
if (len == -1) {
break;
}
fos.write(buf, 0, len);
curPos = curPos + len;
if (curPos > endPos) {
readByte += len - (curPos - endPos) + 1; //获取正确读取的字节数
} else {
readByte += len;
}
}
System.out.println("线程" + id + "已经下载完毕。");
this.finished = true;
bis.close();
fos.close();
} catch (IOException ex) {
ex.printStackTrace();
throw new RuntimeException(ex);
}
}
上面的代码就是根据startPos和endPos对文件机型写操作,每个线程都有自己独立的一个资源块,从startPos到endPos。上面的方式就是线程下载的核心,多线程搞定后,接下来就是实现断点恢复的功能,其实断点恢复无非就是记录下每个线程完成到哪个未知,在这里我就是使用curPos进行的记录,大家在上面的代码就应该可以看到,我会记录下每个线程的curPos,然后在线程重新启动的时候,就把curPos当成是startPos,而endPost则不变即可,大家有没注意到run方法里有一段这样的代码:
java代码:
1.if (isNewThread) { //判断是否断点,如果true,代表是一个新的下载线程,而不是断点恢复
2. con.setRequestProperty("Range", "bytes=" + startPos + "-" + endPos);//设置获取资源数据的范围,从startPos到endPos
3. fos = new RandomAccessFile(file, "rw"); //创建RandomAccessFile
4. fos.seek(startPos); //从startPos开始
5.} else {
6. con.setRequestProperty("Range", "bytes=" + curPos + "-" + endPos);//使用curPos替代startPos,其他都和新创建一个是一样的。
7. fos = new RandomAccessFile(dlTask.getFile(), "rw");
8. fos.seek(curPos);
9.}
上面就是断点恢复的做法了,和新创建一个线程没什么不同,只是startPos不一样罢了,其他都一样,不过仅仅有这个还不够,因为如果程序关闭的话,这些信息又是如何保存呢?例如文件名啊,每个线程的curPos啊等等,大家在使用下载软件的时候,相信都会发现在软件没下载完的时候,在目录下会有两个临时文件,而其中一个就是用来保存下载任务的信息的,如果没有这些信息,程序是不知道该如何恢复下载进度的。而我这里又如何实现的呢?我这个人比较懒,又不想再创建一个文件来保存信息,然后自己又要读取信息创建对象,那太麻烦了,所以我想到了java提供序列化机制,我的想法就是直接把整个DLTask的对象序列化到硬盘上,上面说过DLTask这个类就是用来保存每个任务的信息的,所以我只要在需要恢复的时候,反序列化这个对象,就可以很容易的实现了断点功能,我们来看看这个对象保存的信息:
java代码:
1.public class DLTask extends Thread implements Serializable {
2.
3. private static final long serialVersionUID = 126148287461276024L;
4. private final static int MAX_DLTHREAD_QUT = 10; //最大下载线程数量
5. /**
6. * 下载临时文件后缀,下载完成后将自动被删除
7. */
8. public final static String FILE_POSTFIX = ".tmp";
9. private URL url;
10. private File file;
11. private String filename;
12. private int id;
13. private int Level;
14. private int threadQut; //下载线程数量,用户可定制
15. private int contentLen; //下载文件长度
16. private long completedTot; //当前下载完成总数
17. private int costTime; //下载时间计数,记录下载耗费的时间
18. private String curPercent; //下载百分比
19. private boolean isNewTask; //是否新建下载任务,可能是断点续传任务
20.
21. private DLThread[] dlThreads; //保存当前任务的线程
22.
23.transient private DLListener listener; //当前任务的监听器,用于即时获取相关下载信息
如上代码,这个对象实现了Serializable接口,保存了任务的所有信息,还包括有每个线程对象dlThreads,这样子就可以很容易做到断点的恢复了,让我重新写一个文件保存这些信息,然后在恢复的时候再根据这些信息创建一个对象,那简直是要我的命。这里创建了一个方法,用于断点恢复用:
1.private void resumeTask() {
2. listener = new DLListener(this);
3. file = new File(filename);
4. for (int i = 0; i < threadQut; i++) {
5. dlThreads[i].setDlTask(this);
6. QSEngine.pool.execute(dlThreads[i]);
7. }
8. QSEngine.pool.execute(listener);
9.}
实际上就是减少了先连接资源,然后进行切分资源的代码,因为这些信息已经都被保存在DLTask的对象下了。
看到上面的代码,不知道大家注意到有一个对象DLListener没有,这个对象实际上就是用于监听整个任务的信息的,这里我主要用于两个目的,一个是定时的对DLTask进行序列化,保存任务信息,用于断点恢复,一个就是进行下载速率的统计,平均多长时间进行一个统计。我们先来看下它的代码,这个类也是一个单独的线程:
java代码:
1.public void run() {
2.
3. int i = 0;
4. BigDecimal completeTot = null; //完成的百分比
5. long start = System.currentTimeMillis(); //当前时间,用于记录开始统计时间
6. long end = start;
7.
8. while (!dlTask.isComplete()) { //整个任务是否完成,没有完成则继续循环
9. i++;
10. String percent = dlTask.getCurPercent(); //获取当前的完成百分数
11.
12. completeTot = new BigDecimal(dlTask.getCompletedTot()); //获取当前完成的总字节数
13.
14. //获得当前时间,然后与start时间比较,如果不一样,利用当前完成的总数除以所使用的时间,获得一个平均下载速度
15. end = System.currentTimeMillis();
16. if (end - start != 0) {
17. BigDecimal pos = new BigDecimal(((end - start) / 1000) * 1024);
18. System.out.println("Speed :"
19. + completeTot
20. .divide(pos, 0, BigDecimal.ROUND_HALF_EVEN)
21. + "k/s " + percent + "% completed. ");
22. }
23. recoder.record(); //将任务信息记录到硬盘
24. try {
25. sleep(3000);
26. } catch (InterruptedException ex) {
27. ex.printStackTrace();
28. throw new RuntimeException(ex);
29. }
30.
31. }
32. //以下是下载完成后打印整个下载任务的信息
33. int costTime =+ (int)((System.currentTimeMillis() - start) / 1000);
34. dlTask.setCostTime(costTime);
35. String time = QSDownUtils.changeSecToHMS(costTime);
36.
37. dlTask.getFile().renameTo(new File(dlTask.getFilename()));
38. System.out.println("Download finished. " + time);
39.}
这个方法中的recoder.record()方法的调用就是用于序列化任务对象,其他的代码均为统计信息用的,具体可看注释,record该方法的代码如下:
java代码:
1.public void record() {
2. ObjectOutputStream out = null;
3. try {
4. out = new ObjectOutputStream(new FileOutputStream(dlTask.getFilename() + ".tsk"));
5. out.writeObject(dlTask);
6. out.close();
7. } catch (IOException ex) {
8. ex.printStackTrace();
9. throw new RuntimeException(ex);
10. } finally {
11. try {
12. out.close();
13. } catch (IOException ex) {
14. ex.printStackTrace();
15. throw new RuntimeException(ex);
16. }
17. }
18.
19.}
到这里,大致的代码都完成了,不过以上的代码都是部分片段,只是作为一个参考给大家看下,而且由于本人水平有限,代码很多地方都没有经过过多的考虑,没有经过优化,仅仅只是自娱自乐,所以可能有很多地方都写的很烂,这个程序也缺乏很多功能,连界面都没有,所以整个程序的代码就不上传了,免得丢人,呵呵。希望对有兴趣的朋友尽到一点帮助吧。
相关推荐
以下将详细解释如何使用Java实现这一功能。 首先,我们需要连接到资源服务器并获取资源信息。在Java中,我们可以使用`java.net.URL`和`java.net....通过这些类和方法,我们可以构建出一个功能完善的多线程HTTP下载器。
在Java中,实现多线程主要依赖于`java.lang.Thread`类或者`java.util.concurrent`包中的`ExecutorService`和`Future`等接口。下面我们将详细探讨这些知识点: 1. **创建线程**: - **继承Thread类**:自定义一个类...
下面我们将深入探讨如何使用Java实现多线程下载。 1. **线程基础知识**: - 线程是操作系统调度的基本单位,一个进程中可以有多个线程并发执行。 - Java提供了内置的线程支持,包括`Thread`类和`Runnable`接口,...
在本案例中,我们将探讨如何使用Java实现文件的多线程下载,这对于处理大文件或者在网络带宽有限的情况下尤其有用。通过多线程下载,可以将文件分割成多个部分并同时下载,从而缩短整体下载时间。 首先,我们需要...
在Java编程中,多线程下载文件是一种优化大文件下载速度和效率的技术。它通过将一个大文件分割成多个小部分,然后同时启动多个线程分别下载这些部分,从而达到加速下载的效果。以下是对这个主题的详细解释: 1. **...
Java HTTP多线程下载器是一种优化的网络资源下载工具,它利用Java编程语言实现了HTTP协议的下载功能,并结合多线程技术以提高下载速度。在本文中,我们将深入探讨Java HTTP多线程下载器的工作原理、核心概念以及实现...
本项目"Java实现多线程下载源代码"正是这样一个示例,它使用了NetBeans IDE创建用户界面,使得用户能够更加直观地管理和监控下载过程。 首先,我们来深入理解Java中的多线程。在Java中,线程是程序执行的最小单位,...
在Java中实现多线程下载器涉及许多关键概念和技术,包括线程、并发控制、网络I/O以及进度监控。 1. **线程**:在Java中,线程是程序执行的最小单位,每个线程负责一部分文件的下载任务。Java提供两种方式创建线程:...
在实现多线程下载时,需要考虑以下几个方面: 1. **任务分配**:根据文件大小动态决定线程数量,避免过多线程导致的资源浪费和系统性能下降。 2. **分块下载**:确定每个线程下载的文件块大小,确保线程间工作负载...
采用apache commons开发包,实现了FTP多线程下载,并有简单的包括进度条的界面,代码的运行:1 把自己的PC配置成FTP服务器(只需下载Serc-U软件)2 建立用户,用户名:zhangyu,密码:123,设置根目录,并拷贝进一些...
本篇文章将深入探讨如何使用Java实现多线程文件传输,并涵盖以下几个关键知识点: 1. **线程基础**:在Java中,线程是程序执行的基本单元,可以通过实现`Runnable`接口或继承`Thread`类来创建。`Runnable`接口更...
刚学完多线程和线程池,以及线程爱好者。 使用场景及目标: 大文件的快速下载,经测试在100M带宽的网络下,下载一个40M的文件,不超过两秒钟,当然还得根据自己电脑的配置来讲。 其他说明: 文件如果特别小,而...
1. 把每个下载文件切成若干个块...2. HTTP Header里增加Range,如果服务器返回Cotent-Range 说明服务器支持文件定位,可以实现多线程下载 详细看这里 http://blog.csdn.net/maoxiang/archive/2010/04/02/5446293.aspx
在Java中实现多线程下载,主要涉及到以下几个核心知识点: 1. **线程基础**:首先,你需要了解Java中的`Thread`类或实现`Runnable`接口来创建和管理线程。每个线程负责下载文件的一个部分。 2. **文件分块**:在...
java实现多线程经典模型生产者消费java实现多线程经典模型生产者消费java实现多线程经典模型生产者消费java实现多线程经典模型生产者消费java实现多线程经典模型生产者消费java实现多线程经典模型生产者消费java实现...
【Java多线程简单下载器】是一个初学者的编程作业,虽然代码可能较为混乱,但其核心功能已经实现,即通过多线程技术进行文件的下载。在Java中,多线程是并发处理的重要手段,它允许多个任务在同一时间执行,从而提高...
在Java中实现多线程下载主要涉及以下几个核心概念和技术: 1. **线程(Thread)**:Java中的`Thread`类是进行多线程操作的基础。每个线程负责下载文件的一部分。创建一个新的线程对象并调用其`start()`方法可以启动...