`

java网络编程之Http多线程下载应用实例

阅读更多

本demo 通过RandomAccessFile, URLConnection和多线程机制实现了Http下载功能. 这里 可以下载到完整的java代码工程:  http://download.csdn.net/detail/hejiangtao /4029935.  相对于别的网上的例子来看, 本demo 是可运行的, 可以判断网络资源是否支持分段下载. 你是否遇到了java下载的图片 显示不出来或者RAR解压不了的情况, 可以参考本demo的解决方案 偷笑

设计思路:

1. 首先读取文件的长度, 并判断网站是否支持分段下载

2. 如果支持分段下载则创建多个线程同时下载该文件,否则使用单线程下载

3. 在各个线程中,分别使用 RandomAccessFile对象写入对应的文件位置

具体实现解析:

      1. 为了方便理解,先把util类贴出来.

SysValue.java:

在实际应用中,这些参数应该可以在界面配置或者配置文件配置.

[java] view plain copy
  1. package  com.ross.httpdownload.util;  
  2. /**  
  3.  * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com  
  4.  * Date: 2012-1-18  
  5.  * Since: MyJavaExpert v1.0  
  6.  * Description:  
  7.  */   
  8. public   class  SysValue  
  9. {  
  10.     //constant values   
  11.     //maximum number of the download thread   
  12.     public   static   int  MAX_Num_Of_Thread =  3 ;  
  13.     //download file store path   
  14.     public   static  String Save_As_Path =  "E:/myspace/download/" ;  
  15.     //buffer size   
  16.     public   static   int  Buffer_Size =  1024 ;      
  17. }  
HttpProcess.java:

将Http相关的公共操作放在这个类里面,避免写重复代码.

[java] view plain copy
  1. package  com.ross.httpdownload.util;  
  2. import  java.io.IOException;  
  3. import  java.net.*;  
  4. /**  
  5.  * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com  
  6.  * Date: 2012-1-18  
  7.  * Since: MyJavaExpert v1.0  
  8.  * Description: This class will provide the common process of http  
  9.  */   
  10. public   class  HttpProcess  
  11. {  
  12.     /**  
  13.      * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com  
  14.      * Description: get an object of opened URL connection  
  15.      * @param sURL: completed URL string  
  16.      * @return oHttpURLCon: an object of opened URL connection  
  17.      * @throws IOException  
  18.      */   
  19.     public  URLConnection  getURLConnection(String sURL)  throws  IOException  
  20.     {  
  21.         URLConnection  oURLCon = null ;  
  22.         // replace space with '+' in the URL string   
  23.         if  ( null  != sURL)  
  24.         {  
  25.             sURL = sURL.replaceAll("\\s" "+" );  
  26.             // generate URL   
  27.             URL oURL = new  URL(sURL);  
  28.             oURLCon = (URLConnection) oURL.openConnection();  
  29.         }  
  30.         return  oURLCon;  
  31.     }  
  32.   
  33. }  

    2. 看我们的多线程下载实现

a. 首先看下如何判断网站资源是否支持分段下载

很 多网络资源,即使在java里面设置了http的InputStream流的范围后, 取的的流的长度还是整个文件的长度. 针对这种情况我就认为该资源不支持分段下载,也就是无法多线程下载了(使用原来的方式下载你就发现下载的图片只能显示一半,或者不能显示,RAR也是损坏 的, 字节不对吗 :)). 我也试过通过使用InputStream 流的skip方法跳过前面内容的方式实现多线程,但是事实证明,skip太不靠谱,跳的步长并不是一定的,哎什么玩意,只好放弃.

[java] view plain copy
  1. /**  
  2.  * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com  
  3.  * Description: Check whether the server support multiple thread download or not  
  4.  * @param sURL: the download file URL  
  5.  * @return bRet: true - support, false - not support  
  6.  * @throws IOException   
  7.  */   
  8. public   boolean  isSupportMultipleThreadDown(String sURL)  throws  IOException  
  9. {  
  10.     boolean  bRet =  true ;  
  11.    // get URL connection   
  12.     URLConnection oURLCon = oHttpProcess.getURLConnection(sURL);  
  13.    // set the resource range   
  14.     oURLCon.setRequestProperty("Range" "bytes="  +  0  +  "-"  +  1023 );  
  15.   
  16.     if  ( 1024  < oURLCon.getContentLength())  
  17.     {  
  18.         bRet = false ;  
  19.     }  
  20.     return  bRet;  
  21. }  

b. 然后我们看下如何,将文件分段下载

1) 首先是预处理

先是做了个简单的判断,防止线程过多或者小于1; 然后判断网络资源是否支持分段下载,如果不支持则将线程数重置为1; 在获取整个文件长度后, 使用总长度除以线程数得出每个线程负责下载的字节范围.

2) 使用线程池管理并启动多线程下载

DownloadTread就是我们的线程下载实现类, 后面会单独说明. 众所周知java下标是从0开始的,所以各个线程分配字节范围的时候,要考虑进去. 将对应分配的字节范围,URL,文件名信息设置给DownloadTread对象后,使用ExecutorService启动线程.

3)当然不要忘记在finally里面调用线程池的关闭方法.

[java] view plain copy
  1. /**  
  2.  * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com  
  3.  * Description: download tool entry method  
  4.  * @param sURL: the completed URL of the file, which is wanted to download.   
  5.  * @param Num: Number of concurrent download thread  
  6.  * @param sSaveAsName: the the file name saved in disk, like: myphoto.jpg  
  7.  */   
  8. public   void  download(String sURL,  int  iNumOfThread, String sSaveAsName)  
  9. {  
  10.     long  lFileLen =  0 ;  
  11.     long  lFileSegementLen =  0 ;  
  12.     long  iLeft =  0 ;  
  13.     URLConnection oURLCon = null ;  
  14.     String sFullFileName = SysValue.Save_As_Path + sSaveAsName;  
  15.   
  16.     if  (iNumOfThread > SysValue.MAX_Num_Of_Thread)  
  17.     {  
  18.         iNumOfThread = SysValue.MAX_Num_Of_Thread;  
  19.     }  
  20.     if  (iNumOfThread <  1 )  
  21.     {  
  22.         iNumOfThread = 1 ;  
  23.     }  
  24.   
  25.     // build the thread pool to manage the thread   
  26.     ExecutorService oExecService = Executors  
  27.             .newFixedThreadPool(iNumOfThread);  
  28.     try   
  29.     {  
  30.         if  (!isSupportMultipleThreadDown(sURL))  
  31.         {  
  32.             System.out  
  33.                     .println("This site do not support multiple thread download, reset the thread to 1" );  
  34.             iNumOfThread = 1 ;  
  35.         }  
  36.         // get URL connection   
  37.         oURLCon = oHttpProcess.getURLConnection(sURL);  
  38.         // get the length of the file   
  39.         lFileLen = oURLCon.getContentLength();  
  40.         // split the length of the file according to the number of thread.   
  41.         lFileSegementLen = lFileLen / iNumOfThread;  
  42.         iLeft = lFileLen % iNumOfThread;  
  43.   
  44.         for  ( int  i =  0 ; i < iNumOfThread; i++)  
  45.         {  
  46.             // initial the thread data   
  47.             DownloadThread oDownloadThread = new  DownloadThread();  
  48.             oDownloadThread.setIThreadId(i);  
  49.             // Start index of the current thread   
  50.             oDownloadThread.setLStartIndex(i * lFileSegementLen);  
  51.   
  52.             // last thread will take all the rest content   
  53.             if  (i == (iNumOfThread -  1 ))  
  54.             {  
  55.                 oDownloadThread.setLEndIndex((i + 1 ) * lFileSegementLen  
  56.                         + iLeft - 1 );  
  57.             }  
  58.             else   
  59.             {  
  60.                 oDownloadThread  
  61.                         .setLEndIndex((i + 1 ) * lFileSegementLen -  1 );  
  62.             }  
  63.             // set save file name with path   
  64.             oDownloadThread.setSFullFileName(sFullFileName);  
  65.             // set URL   
  66.             oDownloadThread.setSFileURL(sURL);  
  67.             // execute the download thread   
  68.             oExecService.execute(oDownloadThread);  
  69.         }  
  70.         System.out.println("the file is stored as \""  + sFullFileName  
  71.                 + "\"." );  
  72.         System.out.println("the file length is "  + lFileLen +  "." );  
  73.     }  
  74.     catch  (IOException e)  
  75.     {  
  76.         System.out.println("download the file failed, IO Exception"   
  77.                 + e.getMessage());  
  78.         e.printStackTrace();  
  79.     }  
  80.     finally   
  81.     {  
  82.         oExecService.shutdown();  
  83.     }  
  84. }  

c.最后就是我们的核心下载线程实现类了

1) DownloadTread类实现了Runnalbe抽象接口, 实现了其run()方法.

2) 两个关键的地方一是通过使用URLConnection 的setRequestProperty方法设置本线程读取的流的范围, 另外一个是通过RandomAccessFile 的seek方法或者当前线程写入文件的位置, 一个字节都不能错哦,错了你的文件就有差错了, 还是要记住java下标是从0开始的.

3)然后通过循环从URLConnection的InputStream里面通过 buffer一组一组的读出来, 通过RandomAccessFile 对象一组一组的写入到目标文件里面(代码里面判断实际读取长度部分是不需要的, 可以去掉, 是我探索不支持分段下载网络资源的多线程下载方式时留下的).

4)需要提一下的是run()方法是没有输入参数的,所以我们通过增加类字段的方式将参数传入该方法.

[java] view plain copy
  1. package  com.ross.httpdownload;  
  2. import  java.io.*;  
  3. import  java.net.*;  
  4. import  java.text.*;  
  5. import  java.util.Date;  
  6. import  com.ross.httpdownload.util.*;  
  7. /**  
  8.  * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com  
  9.  * Date: 2012-1-18  
  10.  * Since: MyJavaExpert v1.0  
  11.  * Description: download thread implementation.  
  12.  */   
  13. public   class  DownloadThread  implements  Runnable  
  14. {  
  15.     private  String sFileURL;  
  16.     private   long  lStartIndex;  
  17.     private   long  lEndIndex;  
  18.     private   int  iThreadId;  
  19.     private  String sFullFileName;  
  20.     private  HttpProcess oHttpProcess;  
  21.     /**  
  22.      * Description: default constructor, it is used for initializing the fields.  
  23.      */   
  24.     public  DownloadThread()  
  25.     {  
  26.         oHttpProcess = new  HttpProcess();  
  27.     }  
  28.     /**  
  29.      * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com  
  30.      * Description: it will implement the download logic  
  31.      */   
  32.     public   void  run()  
  33.     {  
  34.         System.out.println("The download thread "   
  35.                 + this .iThreadId  
  36.                 + " is started at "   
  37.                 + (new  SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ))  
  38.                         .format(new  Date()));  
  39.         System.out.println("The download index of "  +  this .iThreadId +  " is "   
  40.                 + this .lStartIndex +  " to "  +  this .lEndIndex);  
  41.   
  42.         // content length   
  43.         long  lContentlen =  0 ;  
  44.         // used to store the new url connection   
  45.         URLConnection oURLCon = null ;  
  46.         // used to store the input stream of the response   
  47.         BufferedInputStream oBIn = null ;  
  48.         // used to store the output writer   
  49.         RandomAccessFile oRAFile = null ;  
  50.         // create the buffer   
  51.         byte [] bBuffer =  new   byte [SysValue.Buffer_Size];  
  52.   
  53.         // get URL connection   
  54.         try   
  55.         {  
  56.             // create a link for each thread   
  57.             oURLCon = oHttpProcess.getURLConnection(this .sFileURL);  
  58.             // allow the user interaction, for example: a verify pop window   
  59.             oURLCon.setAllowUserInteraction(true );  
  60.             // set the resource range   
  61.             oURLCon.setRequestProperty("Range" "bytes="  +  this .lStartIndex  
  62.                     + "-"  +  this .lEndIndex);  
  63.   
  64.             // get the url connection input stream   
  65.             oBIn = new  BufferedInputStream(oURLCon.getInputStream());  
  66.    
  67.             // initialize the random access file object   
  68.             oRAFile = new  RandomAccessFile( this .sFullFileName,  "rw" );   
  69.             oRAFile.seek(this .lStartIndex);  
  70.   
  71.             // read the stream from http connection   
  72.             int  iLen =  0 ;  
  73.             int  iActualLen =  0 ;  
  74.             while  (iActualLen < ( this .lEndIndex -  this .lStartIndex +  1 ))  
  75.             {  
  76.                 iLen = oBIn.read(bBuffer, 0 , SysValue.Buffer_Size);  
  77.                 if  (- 1  == iLen)  
  78.                 {  
  79.                     break ;  
  80.                 }  
  81.   
  82.                 // write the read data to file   
  83.                 oRAFile.write(bBuffer, 0 , iLen);  
  84.   
  85.                 // move the position mark   
  86.                 iActualLen = iActualLen + iLen;  
  87.             }  
  88.             System.out.println("Thread "  +  this .iThreadId  
  89.                     + " download is finished, totaly: "  + iActualLen);  
  90.         }  
  91.         catch  (IOException e)  
  92.         {  
  93.             System.out.println("download the file failed, IO Exception"   
  94.                     + e.getMessage());  
  95.             e.printStackTrace();  
  96.         }  
  97.         finally   
  98.         {  
  99.             if  ( null  != oRAFile)  
  100.             {  
  101.                 try   
  102.                 {  
  103.                     oRAFile.close();  
  104.                 }  
  105.                 catch  (IOException e)  
  106.                 {  
  107.                     System.out  
  108.                             .println("close object of RandomAccessFile failed, IO Exception"   
  109.                                     + e.getStackTrace());  
  110.                 }  
  111.             }  
  112.   
  113.             if  ( null  != oBIn)  
  114.             {  
  115.                 try   
  116.                 {  
  117.                     oBIn.close();  
  118.                 }  
  119.                 catch  (IOException e)  
  120.                 {  
  121.                     System.out  
  122.                             .println("close input stream of http connection failed, IO Exception"   
  123.                                     + e.getStackTrace());  
  124.                 }  
  125.             }  
  126.         }  
  127.         System.out.println("The download thread "   
  128.                 + this .iThreadId  
  129.                 + " is ended at "   
  130.                 + (new  SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ))  
  131.                         .format(new  Date()));  
  132.     }  
  133.    //省略了set/get方法     
  134. }  

来测试下我们的实现:

看下我们的main函数, 我们就通过3个例子来测试我们的实现: 一个可以多线程下载的zip文件,一个可以多线程下载的图片,一个不能多线程下载图片.

MyMain.java:

[java] view plain copy
  1. package  com.ross.httpdownload;  
  2. import  com.ross.httpdownload.util.*;  
  3. /**  
  4.  * Author: Jiangtao He; Email: ross.jiangtao.he@gmail.com  
  5.  * Date: 2012-1-19  
  6.  * Since: MyJavaExpert v1.0  
  7.  * Description: Test the download tool class  
  8.  */   
  9. public   class  MyMain  
  10. {  
  11.     public   static   void  main(String[] args)  
  12.     {  
  13.         // set system configuration, in real software, it can be configured   
  14.         // through GUI, file,etc.   
  15.         SysValue.MAX_Num_Of_Thread = 3 ;  
  16.         SysValue.Save_As_Path = "E:/myspace/download/" ;  
  17.         SysValue.Buffer_Size = 1024 ;  
  18.         //file web address   
  19.         //String sFileURL = "http://xinsheng-image.huawei.com/cn/forumimage/showimage-991441-20990f56c5b11261fa63e969d8d5580c-self.jpg";   
  20.         //String sFileURL = "http://lh5.googleusercontent.com/-F7a0loCDyoQ/AAAAAAAAAAI/AAAAAAAAAFY/TrDxSDdQuhQ/s512-c/photo.jpg";   
  21.         //String sFileURL = "http://xinsheng-image.huawei.com/cn/forumimage/showimage-816607-f61c8fe14fc359d49b044376a0956acd-self.jpg";   
  22.         //String sFileURL = "http://3.bp.blogspot.com/-oVO24VW8F6M/TxJ9O0opP3I/AAAAAAAAANY/anpu8S4FAC4/s640/100_9223.JPG";   
  23.         String sFileURL = "http://hi.csdn.net/attachment/201201/1/0_1325433530Bg5e.gif" ;  
  24.         sFileURL = "http://west263.newhua.com:82/down/jia-audio-converter.zip" ;  
  25.         //file save as name   
  26.         String sFileName = sFileURL.substring(sFileURL.lastIndexOf("/" ) +  1 );  
  27.          
  28.         // create a object of download tool   
  29.         HttpDownloadTool oHttpDownloadTool = new  HttpDownloadTool();  
  30.         // download the file   
  31.         oHttpDownloadTool.download(sFileURL, 3 , sFileName);  
  32.     }  
  33. }  

a. 可以多线程下载的zip文件测试

URL: http://west263.newhua.com:82/down/jia-audio-converter.zip

线程数: 3

控制台打印信息:

[plain] view plain copy
  1. the file is stored as "E:/myspace/download/jia-audio-converter.zip".  
  2. the file length is 5720120.  
  3. The download thread 1 is started at 2012-01-21 00:49:23  
  4. The download index of 1 is 1906706 to 3813411  
  5. The download thread 0 is started at 2012-01-21 00:49:23  
  6. The download index of 0 is 0 to 1906705  
  7. The download thread 2 is started at 2012-01-21 00:49:23  
  8. The download index of 2 is 3813412 to 5720119  
  9. Thread 0 download is finished, totaly: 1906706  
  10. The download thread 0 is ended at 2012-01-21 00:58:09  
  11. Thread 1 download is finished, totaly: 1906706  
  12. The download thread 1 is ended at 2012-01-21 01:06:17  
  13. Thread 2 download is finished, totaly: 1906708  
  14. The download thread 2 is ended at 2012-01-21 01:07:05  
下载的文件抓图:

b.可以多线程下载的图片测试

URL: http://hi.csdn.net/attachment/201201/1/0_1325433530Bg5e.gif

线程数: 3

控制台打印信息:

[plain] view plain copy
  1. the file is stored as "E:/myspace/download/0_1325433530Bg5e.gif".  
  2. the file length is 23304.  
  3. The download thread 2 is started at 2012-01-21 02:03:00  
  4. The download thread 0 is started at 2012-01-21 02:03:00  
  5. The download index of 2 is 15536 to 23303  
  6. The download index of 0 is 0 to 7767  
  7. The download thread 1 is started at 2012-01-21 02:03:00  
  8. The download index of 1 is 7768 to 15535  
  9. Thread 2 download is finished, totaly: 7768  
  10. The download thread 2 is ended at 2012-01-21 02:03:01  
  11. Thread 1 download is finished, totaly: 7768  
  12. The download thread 1 is ended at 2012-01-21 02:03:01  
  13. Thread 0 download is finished, totaly: 7768  
  14. The download thread 0 is ended at 2012-01-21 02:03:02  
下载的文件抓图:

c. 不能多线程下载图片

URL: http://3.bp.blogspot.com/-oVO24VW8F6M/TxJ9O0opP3I/AAAAAAAAANY/anpu8S4FAC4/s640/100_9223.JPG

线程数: 3

控制台打印信息:

[plain] view plain copy
  1. This site do not support multiple thread download, reset the thread to 1  
  2. the file is stored as "E:/myspace/download/100_9223.JPG".  
  3. the file length is 170398.  
  4. The download thread 0 is started at 2012-01-21 02:06:54  
  5. The download index of 0 is 0 to 170397  
  6. Thread 0 download is finished, totaly: 170398  
  7. The download thread 0 is ended at 2012-01-21 02:06:55  
下载的文件抓图:

到这里整个demo完成了.

注: 转载请注明出处: http://hejiangtao.iteye.com 用于商业得给我分成大笑

5
0
分享到:
评论
1 楼 砚台观月 2016-07-15  
你好,例子还有吗,我想要份学习看下。提供的链接找不到了。

相关推荐

    java多线程编程实例

    java多线程编程实例

    java多线程编程实例 (源程序)

    java多线程编程实例 java多线程编程实例 (源程序)

    Java多线程编程实战指南(核心篇)

    Java多线程编程实战指南...本书以基本概念、原理与方法为主线,辅以丰富的实战案例和生活化实例,并从Java虚拟机、操作系统和硬件多个层次与角度出发,循序渐进、系统地介绍Java平台下的多线程编程核心技术及相关工具。

    Java多线程编程实例

    这是Java多线程编程实例一书的全部随书源码,这本书是很早的JAVA多线程编程指导书,虽然很老但是很经典,直到现在还很热,很有参考价值。

    《Java多线程编程实例》随书源码

    《Java多线程编程实例》随书源码《Java多线程编程实例》随书源码《Java多线程编程实例》随书源码

    java多线程小程序实例

    java多线程小程序实例 java多线程小程序实例

    java 多线程编程实例

    内容是两个多线程编程的demo: 1.一个线程添加,一个线程减少 2.a线程呼叫b线程,b线程呼叫c线程,c线程呼叫a线程。 能学到: 在学习多线程编程中,我们要把大家用的对象独立出来,然后作为一个 变量传递到各个线程...

    Java2.0网络多线程编程实例教程(光盘源代码)

    题名 Java2.0网络多线程编程实例教程 编号[ISBN] 7-900056-38-6 作者 李荣贵等编 出版者 希望电子版

    java多线程编程实例_Source

    java多线程编程实例源码,17章节,上百实例源码

    java多线程编程-详细炒作例子

    java多线程编程详解 里面具有详细的沼泽实例。

    java多线程的讲解和实战

    详细的讲解了java多线程的原理,并配有代码进行实战,适合java初学者和想对多线程有进一步了解的人。

    Java 高并发多线程编程系列案例代码

    Java 高并发多线程编程系列案例代码 & 教程 & 面试题集锦! !! 包括但不限于线程安全性, atomic包下相关类、CAS原理、Unsafe类、synchronized关键字等的使用及注意事项,

    Java网络编程期末考试复习题库+答案

    Java网络编程期末考试复习题库+答案 java网络编程考试题目,内含有Java网络编程期末复习题(I/O、多线程、网络编程、数据库操作),有答案

    java多线程编程实战指南 核心篇 代码

    《Java多线程编程实战指南(核心篇)》以基本概念、原理与方法为主线,辅以丰富的实战案例和生活化实例,并从Java虚拟机、操作系统和硬件多个层次与角度出发,循序渐进、系统地介绍Java平台下的多线程编程核心技术及...

    java多线程编程实例.rar

    java多线程编程实例 一些java多线程编程的实用小例子

    java多线程并发编程例子

    关于java.util.concurrent多线程核心包内各种线程资源的使用场景例子

    简易聊天程序_java网络编程多线程

    java编译的控制台版多人简易聊天程序,运用了ServerSocket和Socket网络编程,还用了多线程,I/O流等技术.可以私聊,可以群聊!

    java多线程编程_java多线程_

    2.讲解了在多线程中对并发访问的控制, 主要就是synchronized的使用, 由于此关键字在使用上非常灵活, 所以书中用了很多案例来介绍此关键字的使用, 为读者学习同步相关内容打好坚实的基础。3.介绍线程并不是孤独的...

    java多线程核心技术

    结合大量实例,全面讲解Java多线程编程中的并发访问、线程间通信、锁等最难突破的核心技术与应用实践 Java多线程无处不在,如服务器、数据库、应用。多线程可以有效提升计算和处理效率,大大提升吞吐量和可伸缩性,...

    java并发编程2

    java并发编程pdf文档第二部分:Java并发编程实战.pdf、Java多线程编程核心技术.pdf、实战Java高并发程序设计.pdf

Global site tag (gtag.js) - Google Analytics