参考资料

疯狂Java讲义IO操作篇,多线程篇。课后题讲义。

业务背景

1、启动程序时,检测任务列表是否有没有完成的下载任务,有的话,呈现出下载任务

2、有任务的话,继续下载,没有的话建立下载任务。

3、暂停下载,关闭下载任务的所有线程

3、开始下载,唤醒所有的下载任务。

基本步骤

讲义实现的步骤很明确,而且逻辑也很严谨。自己做主张,把讲义的代码改了改,发现不能改啊,改了会有好多麻烦。讲义的代码结构设计很棒。

1、建立远程连接,获取下载文件的大小。

2、根据大小和线程数,循环操作生成块实体。

3、根据块文件实体集合,产生相应的4个块文件,启动4个线程下载文件。

4、等待子线程结束,根据块文件实体集合的begin位置,合成一个文件。

扩展

1、当停止下载时,把下载信息序列化到文件中。

2、开始下载时,反序列化文件,开始下载。

实现节点:

在实现过程中,一方面是参照源码,另一方面根据实现的几个节点进行百度,看书。

1、查找如何新建线程,主线程如何等待子线程结束,再进行下一步。

2、文件如何分块,使用RandomAccessFile对象

3、建立HTTP连接,使用HttpUrlConnection对象

总结:针对一个业务流程,需要设计业务模型,然后根据业务模型,设计相应的代码结构,也就是说设计模式。针对业务实现的每个点要考虑到耦合关系,拆分关系,业务关联的关系,业务应该拆开,还是联合。这些地方很难。反而最后的选择使用的技术,代码实现较为简单。其中最难的过程是设计业务模型。

再往下全部是代码了,可以略过。

实体

public class PartModel implements Serializable {//不同jvm防止反序列化出错private static final long serialVersionUID = 1L;private transient static SnowflakeIdWorker sfiw = new SnowflakeIdWorker(0,1L);
//    使用雪花算法,防止ID碰撞private Long id  = sfiw.nextId();//字节开始位置private long begin;//字节长度private long length;//实际内容长度private long curentLength;private long end;public PartModel(){}public PartModel(long begin, long length, long currentLength){this.length = length;this.begin  = begin;this.curentLength  = currentLength;}
public class ResourceModel implements Serializable {private static final long serialVersionUID = 1L;private String sourcePath;private String sourceName;private List<PartModel> partList;private int threadCount;private int downState;private String targetName;private String targetPath;private long size = -1;private Date downloadDate = new Date();public ResourceModel(){}public ResourceModel(String sourcePath,String sourceName,String targetName,String targetPath){this.sourceName = sourceName;this.sourcePath = sourcePath;this.targetName = targetName;this.targetPath = targetPath;}

下载工具类

public class DownUtil {private ResourceModel rm;public DownUtil(ResourceModel rm){this.rm = rm;}public void downLoad(){RandomAccessFile targetRaf = null;try{//根据块文件集合,建立相应的线程if(rm.getPartList() != null){List<PartModel> list = rm.getPartList();int size = list.size();Thread[] threads = new Thread[size];for (int i=0;i<size;i++){PartModel part = list.get(i);RandomAccessFile ras =new RandomAccessFile(rm.getTargetPath() + part.getId() + ".part","rw");threads[i] = new DownThread(ras,part);}for (Thread th:threads) {th.start();//等待子线程完成th.join();}//                使用RandomAccessFile文件合并块文件,也可以使用OutputStreamtargetRaf = new RandomAccessFile(rm.getTargetPath() + rm.getTargetName(),"rw");for (PartModel pm:list) {targetRaf.seek(pm.getBegin());FileInputStream fis = new FileInputStream(rm.getTargetPath() + pm.getId() + ".part");byte[] bts = new byte[1024];int line = 0;while((line=fis.read(bts))!=-1){targetRaf.write(bts,0,line);}fis.close();}}else{System.out.println("分块文件不存在!");}}catch (Exception ex){ex.printStackTrace();}finally {try{if(targetRaf!= null){targetRaf.close();}}catch (Exception e){e.printStackTrace();}}}}

下载线程类

public class DownThread extends Thread {private RandomAccessFile accessFile;private PartModel partModel;public DownThread(RandomAccessFile accessFile,PartModel part) {this.accessFile = accessFile;this.partModel = part;}@Overridepublic void run() {InputStream is = null;HttpURLConnection conn = null;try {String path = "http://localhost:8099/shop/products/1/cs60006.png";URL url = new URL(path);conn = (HttpURLConnection) url.openConnection();long begin = partModel.getBegin();long end = partModel.getEnd();conn.setRequestProperty("Range", "bytes=" + begin + "-" + end);conn.connect();is = conn.getInputStream();byte[] buffer = new byte[1024];int perRead = 0;this.accessFile.seek(this.partModel.getCurentLength());while ((perRead = is.read(buffer)) != -1) {this.accessFile.write(buffer, 0, perRead);this.partModel.setCurentLength(this.partModel.getCurentLength() + perRead);}System.out.println(this.partModel.getId());} catch (IOException ex) {ex.printStackTrace();} finally {try{closeStream(is,conn,this.accessFile);}catch (Exception ex){ex.printStackTrace();}}}private void closeStream(InputStream is,HttpURLConnection conn,RandomAccessFile raf)throws  Exception {if(is != null){is.close();}if(conn != null){conn.disconnect();}if(raf != null){raf.close();}}}

测试开始下载类

public class DownTest {private String serializableFile = "D:" + File.separator + "test.txt";/*** 生成资源对象** @return*/private ResourceModel createResuource() {ResourceModel model = new ResourceModel();String path = "http://localhost:8099/shop/products/1/cs60006.png";model.setSourcePath(path);model.setTargetName("downtest.png");model.setTargetPath("D:");model.setThreadCount(4);return model;}//测试方法public static void main(String[] args) throws Exception {DownTest dt = new DownTest();ResourceModel resourceModel = dt.createResuource();//URL资源定位,建立远程连接。URL url = new URL(resourceModel.getSourcePath());HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setConnectTimeout(5 * 1000);conn.setRequestMethod("GET");conn.setRequestProperty("Accept", "");conn.setRequestProperty("Accept-Language", "zh-CN");conn.setRequestProperty("Charset", "UTF-8");conn.connect();
//        获取文件大小long size = conn.getContentLength();resourceModel.setSize(size);//生成块文件集合resourceModel.setPartList(dt.createPartList(resourceModel));conn.disconnect();new DownUtil(resourceModel).downLoad();//        conn.setRequestProperty("Range","bytes=" + begin);}/*** 反序列生成对象** @return*/public ResourceModel refSeriableResource() {String path = this.serializableFile;ResourceModel rm = null;try {FileInputStream fis = new FileInputStream(path);ObjectInputStream ois = new ObjectInputStream(fis);rm = (ResourceModel) ois.readObject();} catch (Exception ex) {ex.printStackTrace();return rm;}return rm;}/*** 序列化对象到固定文件** @param model*/public void seriableResource(ResourceModel model) {if (model != null) {try {String path = this.serializableFile;OutputStream os = new FileOutputStream(path);ObjectOutputStream oos = new ObjectOutputStream(os);oos.writeObject(model);} catch (Exception e) {e.printStackTrace();}}}/*** 生成集合对象** @param rm* @return*/public List<PartModel> createPartList(ResourceModel rm) {if (rm.getPartList() == null || rm.getPartList().size() == 0) {List<PartModel> list = new ArrayList<>(rm.getThreadCount());int len = rm.getThreadCount();long length = rm.getSize() / len;for (int i = 0; i < len; i++) {PartModel part = new PartModel();long begin = i * length;part.setBegin(begin);part.setCurentLength(0);if (i == len - 1) {part.setEnd(rm.getSize());} else {part.setEnd(begin + length);}list.add(part);}return list;}return rm.getPartList();}/*** 测试序列化*/@Testpublic void testSerializable() {ResourceModel model = new ResourceModel();model.setThreadCount(4);model.setTargetPath("targetPath");model.setTargetName("targetName");model.setSize(4000);model.setSourcePath("sourcepath");List<PartModel> list = new ArrayList<>(4);for (int i = 0; i < 4; i++) {PartModel part = new PartModel();long begin = i * 5444;part.setBegin(begin);part.setCurentLength(0);list.add(part);}model.setPartList(list);this.seriableResource(model);}/*** 测试反序列化*/@Testpublic void testRefSerializable(){ResourceModel model = this.refSeriableResource();System.out.println(model.getSourceName());}

多线程随想录——多线程分块下载文件相关推荐

  1. C# 大文件分块下载

    Response Http 协议中有专门的指令来告知浏览器, 本次响应的是一个需要下载的文件. 格式如下: Content-Disposition: attachment;filename=filen ...

  2. Java 多线程断点下载文件_详解

    本文转载于:http://blog.csdn.net/ibm_hoojo/article/details/6838222 基本原理:利用URLConnection获取要下载文件的长度.头部等相关信息, ...

  3. Qt 之 HTTP 请求 多线程分块下载——上(获取下载文件大小)

    简述 在 Qt 之 HTTP 请求下载(支持断点续传) 文章中我们使用Qt 的方法进行HTTP 请求下载文件,同时能够断点续传,本篇文章是这一篇文章的续篇. 我们一般在网上下载软件.电影.文件等,大都 ...

  4. Python编程:多线程断点下载文件

    一.前言 大多数网站为了服务器宽带均衡使用,会限制单个连接的传输速度.有时需要下载几百MB大小文件,但下载速度只有100~200KB/S的时候,可以采用python开多线程流式下载该文件以加快下载速度 ...

  5. c#.Net MVC服务端下载文件、限制下载速度、大文件下载、对性能的影响、对大文件的支持、对断点续传和多线程下载的支持

    一.ASP.NET文件下载各种方式比较:对性能的影响.对大文件的支持.对断点续传和多线程下载的支持 asp.net里提供了多种方式,从服务器端向客户端写文件流,实现客户端下载文件.这种技术在做防下载系 ...

  6. java 多线程下载 断点_Java 多线程断点下载文件

    基起原根蒂根基理:哄骗URLConnection获取要下载文件的长度.头部等相干信息,并设置响应的头部信息.并且经由过程URLConnection获取输入流,将文件分成指定的块,每一块零丁开辟一个线程 ...

  7. python多线程下载文件

    看到一篇多线程下载的文章,这里把自己的理解写一篇多线程下载的文章. 我们访问http://192.168.10.7/a.jpg时是get请求,response的head包含Content-Length ...

  8. java 多线程下载文件并实时计算下载百分比(断点续传)

    多线程下载文件 多线程同时下载文件即:在同一时间内通过多个线程对同一个请求地址发起多个请求,将需要下载的数据分割成多个部分,同时下载,每个线程只负责下载其中的一部分,最后将每一个线程下载的部分组装起来 ...

  9. java线程下载文件_Java多线程下载文件实例详解

    本文实例为大家分享了Java多线程下载文件的具体代码,供大家参考,具体内容如下 import java.io.File; import java.io.InputStream; import java ...

最新文章

  1. 零基础入门学习Python(34) 面向对象
  2. JVM年轻代参数:-Xmn、-XX:NewSize、-XX:MaxNewSize
  3. 华 为 路 由 器 命 令 大 全
  4. OSMeteorTranslationAPI(百度,有道)对比
  5. 异常检测2——PCA异常检测
  6. 【资源】同济线性代数教材(第五版)
  7. 个人项目集 - Oliver Chu
  8. 金蝶EAS,序时簿ListUI只允许选择一行或至少选择一行记录
  9. java星星闪烁代码_jQuery实现仿QQ头像闪烁效果的文字闪动提示代码
  10. 瑞吉外卖项目学习笔记01
  11. 异常解决——Failed to introspect Class [com.github.pagehelper.autoconfigure.PageHelperAutoConfiguration]
  12. 从坚果3的发布来看,锤子未来的发展将依然艰难
  13. 世界经典电影Top 50
  14. 蓝桥杯大学JAVA题型_蓝桥杯 2020年省赛真题 10月第二场 (Java 大学B组)
  15. 硬件工程师笔试题目1(同上)
  16. 字符串函数的使用和剖析(三)
  17. ftp主动模式和被动模式的区别
  18. python爬虫你们最爱的YY小姐姐,这不爬取下来看看?
  19. Error: mount: 未知的文件系统类型“ntfs”
  20. Android P非SDK接口限制

热门文章

  1. 初学者理解throw和throws
  2. 三星android操作系统耗电量大,三星手机如何省电?提升手机续航能力技巧【详解】...
  3. IDEA中JSP文件out.println报错问题
  4. 【HOOK切换微信聊天窗口】C++
  5. 聚力优创:拼多多可以拒绝退款吗?
  6. 【bug解决】ios 15 type=“search“ 搜索类型的输入框自带搜索图标
  7. 索尼ELF-SR2解析:27英寸、裸眼3D、4K够不够
  8. solid works方程式笔记
  9. 2023.6.8每日一题
  10. 适合学生党的百元蓝牙耳机,蓝牙耳机平价推荐