ImageVerifierCode 换一换
格式:DOCX , 页数:24 ,大小:35.51KB ,
资源ID:19185716      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/19185716.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(支持断点续传java多线程下载Word格式.docx)为本站会员(b****6)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

支持断点续传java多线程下载Word格式.docx

1、public class DownInfo private String threadId;/线程id private BigDecimal startPos=new BigDecimal(0);/开始位置,随着下载增长 private BigDecimal endPos=new BigDecimal(0);/结束位置 public DownInfo(String threadId, long startPos, long endPos) this.threadId=threadId; this.startPos = new BigDecimal(startPos); this.endPos

2、= new BigDecimal(endPos); public long getStartPos() return startPos.longValue(); public long getEndPos() return endPos.longValue(); public void updateStartPos(long size) this.startPos =startPos.add(new BigDecimal(size); public String getThreadId() return threadId; public void setThreadId(String thre

3、adId) this.threadId = threadId;Java实例:利用java多线程断点续传实践2010-07-19 12:22* author:annegu* date:2009-07-16annegu做了一个简单的Http多线程的下载程序,来讨论一下多线程并发下载以及断点续传的问题。这个程序的功能,就是可以分多个线程从目标地址上下载数据,每个线程负责下载一部分,并可以支持断点续传和超时重连。下载的方法是download(),它接收两个参数,分别是要下载的页面的url和编码方式。在这个负责下载的方法中,主要分了三个步骤。第一步是用来设置断点续传时候的一些信息的,第二步就是主要的分多

4、线程来下载了,最后是数据的合并。1、多线程下载:/* */* public String download(String urlStr, String charset) this.charset = charset; long contentLength = 0; CountDownLatch latch = new CountDownLatch(threadNum); long startPos = new longthreadNum; long endPos = 0; try / 从url中获得下载的文件格式与名字 this.fileName = urlStr.substring(urlS

5、tr.lastIndexOf(/) + 1); this.url = new URL(urlStr); URLConnection con = url.openConnection(); setHeader(con); / 得到content的长度 contentLength = con.getContentLength(); / 把context分为threadNum段的话,每段的长度。 this.threadLength = contentLength / threadNum; / 第一步,分析已下载的临时文件,设置断点,如果是新的下载任务,则建立目标文件。在第4点中说明。 startPo

6、s = setThreadBreakpoint(fileDir, fileName, contentLength, startPos); /第二步,分多个线程下载文件 ExecutorService exec = Executors.newCachedThreadPool(); for (int i = 0; i threadNum; i+) / 创建子线程来负责下载数据,每段数据的起始位置为(threadLength * i + 已下载长度) startPosi += threadLength * i; /*/*设置子线程的终止位置,非最后一个线程即为(threadLength * (i +

7、 1) - 1) 最后一个线程的终止位置即为下载内容的长度*/ if (i = threadNum - 1) endPos = contentLength; else endPos = threadLength * (i + 1) - 1; / 开启子线程,并执行。 ChildThread thread = new ChildThread(this, latch, i, startPosi, endPos); childThreadsi = thread; exec.execute(thread); / 等待CountdownLatch信号为0,表示所有子线程都结束。 latch.await(

8、); exec.shutdown(); / 第三步,把分段下载下来的临时文件中的内容写入目标文件中。在第3点中说明。 tempFileToTargetFile(childThreads); catch (InterruptedException e) e.printStackTrace();在ChildThread的构造方法中,除了设置一些从主线程中带来的id, 起始位置之外,就是新建了一个临时文件用来存放当前线程的下载数据。临时文件的命名规则是这样的:下载的目标文件名+”_”+线程编号。现在让我们来看看从网络中读数据是怎么读的。我们通过URLConnection来获得一个http的连接。有些

9、网站为了安全起见,会对请求的http连接进行过滤,因此为了伪装这个http的连接请求,我们给httpHeader穿一件伪装服。下面的setHeader方法展示了一些非常常用的典型的httpHeader的伪装方法。比较重要的有:Uer-Agent模拟从Ubuntu的firefox浏览器发出的请求;Referer模拟浏览器请求的前一个触发页面,例如从skycn站点来下载软件的话,Referer设置成skycn的首页域名就可以了;Range就是这个连接获取的流文件的起始区间。private void setHeader(URLConnection con) con.setRequestPropert

10、y(User-Agent, Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.3) Gecko/2008092510 Ubuntu/8.04 (hardy) Firefox/3.0.3);Accept-Languageen-us,en;q=0.7,zh-cn;q=0.3Accept-EncodingaaAccept-CharsetISO-8859-1,utf-8;q=0.7,*;q=0.7Keep-Alive300Connectionkeep-aliveIf-Modified-SinceFri, 02 Jan 2009 17:00:05 GMTI

11、f-None-Match1261d8-4290-df64d224Cache-Controlmax-age=0Refererhttp:/另外,为了避免线程因为网络原因而阻塞,设置了ConnectTimeout和ReadTimeout,代码、处。setConnectTimeout设置的连接的超时时间,而setReadTimeout设置的是读取数据的超时时间,发生超时的话,就会抛出socketTimeout异常,两个方法的参数都是超时的毫秒数。这里对超时的发生,采用的是等候一段时间重新连接的方法。整个获取网络连接并读取下载数据的过程都包含在一个循环之中(代码处),如果发生了连接或者读取数据的超时,在

12、抛出的异常里面就会sleep一定的时间(代码处),然后continue,再次尝试获取连接并读取数据,这个时间可以通过setSleepSeconds()方法来设置。我们在迅雷等下载工具的使用中,经常可以看到状态栏会输出类似“连接超时,等待*秒后重试”的话,这个就是通过ConnectTimeout,ReadTimeout来实现的。连接建立好之后,我们要检查一下返回响应的状态码。常见的Http Response Code有以下几种:a) 200 OK 一切正常,对GET和POST请求的应答文档跟在后面。b) 206 Partial Content 客户发送了一个带有Range头的GET请求,服务器完

13、成。c) 404 Not Found 无法找到指定位置的资源。这也是一个常用的应答。d) 414 Request URI Too Long URI太长。e) 416 Requested Range Not Satisfiable 服务器不能满足客户在请求中指定的Range头。f) 500 Internal Server Error 服务器遇到了意料不到的情况,不能完成客户的请求。g) 503 Service Unavailable 服务器由于维护或者负载过重未能应答。例如,Servlet可能在数据库连接池已满的情况下返回503。在这些状态里面,只有200与206才是我们需要的正确的状态。所以在

14、代码处,进行了状态码的判断,如果返回不符合要求的状态码,则结束线程,返回主线程并提示报错。假设一切正常,下面我们就要考虑从网络中读数据了。正如我之前在分析mysql的数据库驱动中看的一样,网络中发送数据都是以数据包的形式来发送的,也就是说不管是客户端向服务器发出的请求数据,还是从服务器返回给客户端的响应数据,都会被拆分成若干个小型数据包在网络中传递,等数据包到达了目的地,网络接口会依据数据包的编号来组装它们,成为完整的比特数据。因此,我们可以想到在这里也是一样的,我们用inputStream的read方法来通过网卡从网络中读取数据,并不一定一次就能把所有的数据包都读完,所以我们要不断的循环来从

15、inputStream中读取数据。Read方法有一个int型的返回值,表示每次从inputStream中读取的字节数,如果把这个inputStream中的数据读完了,那么就返回-1。Read方法最多可以有三个参数,byte b是读取数据之后存放的目标数组,off标识了目标数组中存储的开始位置,len是想要读取的数据长度,这个长度必定不能大于b的长度。public synchronized int read(byte b, int off, int len);我们的目标是要把目标地址的内容下载下来,现在分了5个线程来分段下载,那么这些分段下载的数据保存在哪里呢?如果把它们都保存在内存中是非常糟糕

16、的做法,如果文件相当之大,例如是一个视频的话,难道把这么大的数据都放在内存中吗,这样的话,万一连接中断,那前面下载的东西就都没有了?我们当然要想办法及时的把下载的数据刷到磁盘上保存下来。当用bt下载视频的时候,通常都会有个临时文件,当视频完全下载结束之后,这个临时文件就会被删除,那么下次继续下载的时候,就会接着上次下载的点继续下载。所以我们的outputStream就是往这个临时文件来输出了。OutputStream的write方法和上面InputStream的read方法有类似的参数,byte b是输出数据的来源,off标识了开始位置,len是数据长度。public synchronized

17、 void write(byte b, int off, int len) throws IOException;在往临时文件的outputStream中写数据的时候,我会加上一个计数器,每满5000个比特就往文件中flush一下(代码处)。对于输出流的flush,有些要注意的地方,在程序中有三个地方调用了outputStream.flush()。第一个是在循环的读取网络数据并往outputStream中写入的时候,每满5000个byte就flush一下(代码处);第二个是循环之后(代码处),这时候正常的读取写入操作已经完成,但是outputStream中还有没有刷入磁盘的数据,所以要flus

18、h一下才能关闭连接;第三个就是在异常中的flush(代码处),因为如果发生了连接超时或者读取数据超时的话,就会直接跑到catch的exception中去,这个时候outputStream中的数据如果不flush的话,重新连接的时候这部分数据就会丢失了。另外,当抛出异常,重新连接的时候,下载的起始位置也要重新设置(代码处),count就是用来标识已经下载的字节数的,把count+startPosition就是新一次连接需要的下载起始位置了。3、现在每个分段的下载线程都顺利结束了,也都创建了相应的临时文件,接下来在主线程中会对临时文件进行合并,并写入目标文件,最后删除临时文件。这部分很简单,就是一

19、个对所有下载线程进行遍历的过程。这里outputStream也有两次flush,与上面类似,不再赘述。/* */*author by http:/www.guihua.org */private void tempFileToTargetFile(ChildThread childThreads) BufferedOutputStream outputStream = new BufferedOutputStream( new FileOutputStream(fileDir + fileName); / 遍历所有子线程创建的临时文件,按顺序把下载内容写入目标文件中 if (statusErr

20、or) for (int k = 0; k k+) if (childThreadsk.tempFile.length() = 0) childThreadsk.tempFile.delete(); System.out.println(本次下载任务不成功,请重新设置线程数。 break; BufferedInputStream inputStream = new BufferedInputStream( new FileInputStream(childThreadsi.tempFile);Now is file + childThreadsi.id); int len = 0; int c

21、ount = 0; byte b = new byte1024; while (len = inputStream.read(b) != -1) count += len; outputStream.write(b, 0, len); if (count % 5000) = 0) outputStream.flush(); / b = new byte1024; inputStream.close(); / 删除临时文件 if (childThreadsi.status = ChildThread.STATUS_HAS_FINISHED) childThreadsi.tempFile.dele

22、te(); outputStream.close(); catch (FileNotFoundException e) catch (IOException e) 4、最后,说说断点续传,前面为了实现断点续传,在每个下载线程中都创建了一个临时文件,现在我们就要利用这个临时文件来设置断点的位置。由于临时文件的命名方式都是固定的,所以我们就专门找对应下载的目标文件的临时文件,临时文件中已经下载的字节数就是我们需要的断点位置。startPos是一个数组,存放了每个线程的已下载的字节数。 /第一步,分析已下载的临时文件,设置断点,如果是新的下载任务,则建立目标文件。private long setTh

23、readBreakpoint(String fileDir2, String fileName2, long contentLength, long startPos) File file = new File(fileDir + fileName); long localFileSize = file.length(); if (file.exists() file + fileName + has exists! / 下载的目标文件已存在,判断目标文件是否完整 if (localFileSize 0 & tempFileName.startsWith(fileName + ) int fileLongNum = Integer.parseInt(tempFileName .substring(tempFileName.lastIndexOf() + 1, tempFileName.lastIndexOf() + 2); / 为每个线程设置已下载的位置 startPosfileLongNum = filesk.length(); els

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1