本人大四即将毕业的准程序员(JavaSE、JavaEE、android等)一枚,小项目也做过一点,于是乎一时兴起就写了一些工具。
我会在本博客中陆续发布一些平时可能会用到的工具。
代码质量可能不是很好,大家多担待!
代码或者思路有不妥之处,还希望大牛们能不吝赐教哈!
以下代码为本人原创,转载请注明:
本文转载,来自:
JFileDownloader:用于多线程下载网络文件,并保存在本地。
源码如下:
1.JFileDownloader类:主要负责下载的初始化可启动工作。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 package com.wangjie.extrautil.jfiledownloader; 2 3 import java.io.File; 4 import java.net.HttpURLConnection; 5 import java.net.URL; 6 7 /** 8 * 9 * @author wangjie 10 * @version 创建时间:2013-2-7 下午1:40:52 11 */ 12 public class JFileDownloader{ 13 private String urlPath; 14 private String destFilePath; 15 private int threadCount; 16 private JFileDownloadThread[] threads; 17 18 private JFileDownloadListener fileDownloadListener; // 进度监听器 19 private JFileDownloaderNotificationThread notificationThread; // 通知进度线程 20 21 private File destFile; 22 /** 23 * 下载过程中文件的后缀名。 24 */ 25 public final static String DOWNLOADING_SUFFIX = ".jd"; 26 /** 27 * 默认使用的线程数量。 28 * 如果不设置线程数量参数(threadCount),则默认线程启动数量为1,即单线程下载。 29 */ 30 public static final int DEFAULT_THREADCOUNT = 1; 31 /** 32 * 生成JFileDownloader对象。 33 * @param urlPath 要下载的目标文件URL路径 34 * @param destFilePath 要保存的文件目标(路径+文件名) 35 * @param threadCount 下载该文件所需要的线程数量 36 */ 37 public JFileDownloader(String urlPath, String destFilePath, int threadCount) { 38 this.urlPath = urlPath; 39 this.destFilePath = destFilePath; 40 this.threadCount = threadCount; 41 } 42 /** 43 * 生成JFileDownloader对象,其中下载线程数量默认是1,也就是选择单线程下载。 44 * @param urlPath urlPath 要下载的目标文件URL路径 45 * @param destFilePath destFilePath 要保存的文件目标(路径+文件名) 46 */ 47 public JFileDownloader(String urlPath, String destFilePath) { 48 this(urlPath, destFilePath, DEFAULT_THREADCOUNT); 49 } 50 /** 51 * 默认的构造方法,使用构造方法后必须要调用set方法来设置url等下载所需配置。 52 */ 53 public JFileDownloader() { 54 55 } 56 /** 57 * 开始下载方法(流程分为3步)。 58 *
- 59 *
- 检验URL的合法性 60 *
- 计算下载所需的线程数量和每个线程需下载多少大小的文件 61 *
- 启动各线程。 62 *
2.JFileDownloadListener接口:该接口用于监听JFileDownloader下载的进度。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 package com.wangjie.extrautil.jfiledownloader; 2 3 import java.io.File; 4 5 /** 6 * 7 * 该接口用于监听JFileDownloader下载的进度。 8 * 9 * @author wangjie10 * @version 创建时间:2013-2-7 下午2:12:4511 */12 public interface JFileDownloadListener {13 /**14 * 该方法可获得文件的下载进度信息。15 * @author wangjie16 * @param progress 文件下载的进度值,范围(0-100)。0表示文件还未开始下载;100则表示文件下载完成。17 * @param speed 此时下载瞬时速度(单位:kb/每秒)。18 * @param remainTime 此时剩余下载所需时间(单位为毫秒)。19 */20 public void downloadProgress(int progress, double speed, long remainTime);21 /**22 * 文件下载完成会调用该方法。23 * @author wangjie24 * @param file 返回下载完成的File对象。25 * @param downloadTime 下载所用的总时间(单位为毫秒)。26 */27 public void downloadCompleted(File file, long downloadTime);28 }
3.JFileDownloaderNotificationThread类:该线程为通知下载进度的线程。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 package com.wangjie.extrautil.jfiledownloader; 2 3 import java.io.File; 4 import java.math.BigDecimal; 5 6 /** 7 * 该线程为通知下载进度的线程。 8 * 用于在下载未完成时通知用户下载的进度,范围(0-100)。0表示文件还未开始下载;100则表示文件下载完成。 9 * 此时下载瞬时速度(单位:kb/每秒)。 10 * 在完成时返回下载完成的File对象给用户。返回下载所用的总时间(单位为毫秒)给用户。 11 * @author wangjie 12 * @version 创建时间:2013-2-17 下午12:23:59 13 */ 14 public class JFileDownloaderNotificationThread extends Thread{ 15 private JFileDownloadThread[] threads; 16 private JFileDownloadListener fileDownloadListener; 17 private File destFile; 18 private long destFileSize; 19 private boolean isRunning; // 线程运行停止标志 20 private boolean notificationTag; // 通知标志 21 /** 22 * 通过该方法构建一个进度通知线程。 23 * @param threads 下载某文件需要的所有线程。 24 * @param fileDownloadListener 要通知进度的监听器对象。 25 * @param destFile 下载的文件对象。 26 */ 27 public JFileDownloaderNotificationThread(JFileDownloadThread[] threads, 28 JFileDownloadListener fileDownloadListener, File destFile, long destFileSize) { 29 this.threads = threads; 30 this.fileDownloadListener = fileDownloadListener; 31 this.destFile = destFile; 32 this.destFileSize = destFileSize; 33 } 34 35 /** 36 * 不断地循环来就检查更新进度。 37 */ 38 @Override 39 public void run() { 40 isRunning = true; 41 long startTime = 0; 42 if(null != fileDownloadListener){ 43 startTime = System.currentTimeMillis(); // 文件下载开始时间 44 } 45 46 long oldTemp = 0; // 上次已下载数据长度 47 long oldTime = 0; // 上次下载的当前时间 48 49 while(isRunning){ 50 if(notificationTag){ // 如果此时正等待检查更新进度。 51 // 计算此时的所有线程下载长度的总和 52 long temp = 0; 53 for(JFileDownloadThread thread : threads){ 54 temp += thread.currentLength; 55 } 56 // System.out.println("temp: " + temp); 57 // System.out.println("destFileSize: " + destFileSize); 58 // 换算成进度 59 int progress = (int) ((double)temp * 100 / (double)destFileSize); 60 61 // 把进度通知给监听器 62 if(null != fileDownloadListener){ 63 // 计算瞬时速度 64 long detaTemp = temp - oldTemp; // 两次更新进度的时间段内的已下载数据差 65 long detaTime = System.currentTimeMillis() - oldTime; // 两次更新进度的时间段内的时间差 66 // 两次更新进度的时间段内的速度作为瞬时速度 67 double speed = ((double)detaTemp / 1024) / ((double)(detaTime) / 1000); 68 69 // 保留小数点后2位,最后一位四舍五入 70 speed = new BigDecimal(speed).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); 71 72 // 计算剩余下载时间 73 double remainTime = (double)(destFileSize - temp) / speed; 74 if(Double.isInfinite(remainTime) || Double.isNaN(remainTime)){ 75 remainTime = 0; 76 }else{ 77 remainTime = new BigDecimal(remainTime).setScale(0, BigDecimal.ROUND_HALF_UP).longValue(); 78 } 79 80 // 通知监听者进度和速度以及下载剩余时间 81 fileDownloadListener.downloadProgress(progress, speed, (long)remainTime); 82 83 // 重置上次已下载数据长度和上次下载的当前时间 84 oldTemp = temp; 85 oldTime = System.currentTimeMillis(); 86 } 87 88 // 如果下载进度达到100,则表示下载完毕 89 if(100 <= progress){ 90 // 给下载好的文件进行重命名,即去掉DOWNLOADING_SUFFIX后缀 91 String oldPath = destFile.getPath(); 92 File newFile = new File(oldPath.substring(0, oldPath.lastIndexOf("."))); 93 // 检查去掉后的文件是否存在。如果存在,则删除原来的文件并重命名下载的文件(即:覆盖原文件) 94 if(newFile.exists()){ 95 newFile.delete(); 96 } 97 System.out.println(destFile.renameTo(newFile));// 重命名 98 // 通知监听器,并传入新的文件对象 99 if(null != fileDownloadListener){100 fileDownloadListener.downloadCompleted(newFile, System.currentTimeMillis() - startTime);101 }102 isRunning = false; // 文件下载完就结束通知线程。103 }104 notificationTag = false;105 }106 // 设置为每100毫秒进行检查并更新通知107 try {108 Thread.sleep(100);109 } catch (InterruptedException e) {110 e.printStackTrace();111 }112 113 }114 115 }116 /**117 * 调用这个方法,则会使得线程处于待检查更新进度状态。118 * @author wangjie119 */120 public synchronized void notificationProgress(){121 notificationTag = true;122 }123 /**124 * 取消该通知线程125 * @author wangjie126 */127 public void cancelThread(){128 isRunning = false;129 }130 131 132 }
4.JFileDownloadThread类:真正的下载线程,该线程用于执行该线程所要负责下载的数据。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
![](https://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif)
1 package com.wangjie.extrautil.jfiledownloader; 2 3 import java.io.File; 4 import java.io.IOException; 5 import java.io.InputStream; 6 import java.io.RandomAccessFile; 7 import java.net.HttpURLConnection; 8 import java.net.URL; 9 10 /** 11 * 12 * 真正的下载线程,该线程用于执行该线程所要负责下载的数据。 13 * 14 * @author wangjie 15 * @version 创建时间:2013-2-7 上午11:58:24 16 */ 17 public class JFileDownloadThread extends Thread{ 18 private String urlPath; 19 private File destFile; 20 private long startPos; 21 /** 22 * 此线程需要下载的数据长度。 23 */ 24 public long length; 25 /** 26 * 此线程现在已下载好了的数据长度。 27 */ 28 public long currentLength; 29 30 private JFileDownloaderNotificationThread notificationThread; 31 private boolean isRunning = true; 32 33 /** 34 * 构造方法,可生成配置完整的JFileDownloadThread对象 35 * @param urlPath 要下载的目标文件URL 36 * @param destFile 要保存的目标文件 37 * @param startPos 该线程需要下载目标文件第几个byte之后的数据 38 * @param length 该线程需要下载多少长度的数据 39 * @param notificationThread 通知进度线程 40 */ 41 public JFileDownloadThread(String urlPath, File destFile, long startPos, 42 long length, JFileDownloaderNotificationThread notificationThread) { 43 this.urlPath = urlPath; 44 this.destFile = destFile; 45 this.startPos = startPos; 46 this.length = length; 47 this.notificationThread = notificationThread; 48 } 49 /** 50 * 该方法将执行下载功能,并把数据存储在目标文件中的相应位置。 51 */ 52 @Override 53 public void run() { 54 RandomAccessFile raf = null; 55 HttpURLConnection conn = null; 56 InputStream is = null; 57 try { 58 // URL url = new URL("http://localhost:8080/firstserver/files/hibernate.zip"); 59 URL url = new URL(urlPath); 60 conn = (HttpURLConnection)url.openConnection(); 61 conn.setConnectTimeout(20 * 1000); 62 is = conn.getInputStream(); 63 raf = new RandomAccessFile(destFile, "rw"); 64 raf.setLength(conn.getContentLength()); // 设置保存文件的大小 65 // raf.setLength(conn.getInputStream().available()); 66 67 // 设置读入和写入的文件位置 68 is.skip(startPos); 69 raf.seek(startPos); 70 71 currentLength = 0; // 当前已下载好的文件长度 72 byte[] buffer = new byte[1024 * 1024]; 73 int len = 0; 74 while(currentLength < length && -1 != (len = is.read(buffer))){ 75 if(!isRunning){ 76 break; 77 } 78 if(currentLength + len > length){ 79 raf.write(buffer, 0, (int)(length - currentLength)); 80 currentLength = length; 81 notificationThread.notificationProgress(); // 通知进度线程来更新进度 82 return; 83 }else{ 84 raf.write(buffer, 0, len); 85 currentLength += len; 86 notificationThread.notificationProgress(); // 通知进度线程来更新进度 87 } 88 } 89 } catch (Exception e) { 90 e.printStackTrace(); 91 } finally{ 92 try { 93 is.close(); 94 raf.close(); 95 conn.disconnect(); 96 } catch (IOException e) { 97 e.printStackTrace(); 98 } 99 }100 101 }102 /**103 * 取消该线程下载104 * @author wangjie105 */106 public void cancelThread(){107 isRunning = false;108 }109 110 111 }
使用方法如下:
1 String urlPath = "http://localhost:8080/firstserver/files/test.zip"; 2 String destFilePath = "C:\\Users\\admin\\Desktop\\杂\\临时仓库\\test.zip"; 3 int threadCount = 3; 4 5 JFileDownloader downloader = new JFileDownloader(urlPath, destFilePath, threadCount); 6 //或者: 7 JFileDownloader downloader = new JFileDownloader() 8 .setUrlPath(urlPath) 9 .setDestFilePath(destFilePath)10 .setThreadCount(threadCount)11 .setFileDownloadListener(new JFileDownloadListener() { // 设置进度监听器12 public void downloadProgress(int progress, double speed, long remainTime) {13 System.out.println("文件已下载:" + progress + "%,下载速度为:" + speed + "kb/s,剩余所需时间:" + remainTime + "毫秒");14 }15 public void downloadCompleted(File file, long downloadTime) {16 System.out.println("文件:" + file.getName() + "下载完成,用时:" + downloadTime + "毫秒");17 }18 });19 try {20 downloader.startDownload(); // 开始下载21 } catch (Exception e) {22 e.printStackTrace();23 }