--> 默认最多50个线程 同一文件下载失败延迟超过30秒就结束下载
--> 下载5分钟超时时间,假设5分钟内未下载完就结束下载
--> 依赖 commons-httpclient 与 commons-io 包
package com.leunpha;import org.apache.commons.httpclient.HttpClient;import org.apache.commons.httpclient.methods.GetMethod;import org.apache.commons.httpclient.params.HttpClientParams;import org.apache.commons.io.IOUtils;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import java.io.*;import java.lang.reflect.Method;import java.nio.MappedByteBuffer;import java.nio.channels.FileChannel;import java.security.AccessController;import java.security.PrivilegedAction;import java.util.Observable;import java.util.Timer;import java.util.TimerTask;import java.util.concurrent.CountDownLatch;import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicInteger;import java.util.concurrent.atomic.AtomicLong;import java.util.zip.ZipFile;/** * User: zhoujingjie * Date: 14-4-18 * Time: 下午12:52 */public class Downloader extends Observable { protected String url, savePath; //下载地址与保存路径 protected FileChannel channel; //保存文件的通道 protected long size, perSize; //文件大小与每一个小文件的大小 protected volatile long downloaded; // 已下载的 protected int connectCount; //连接数 protected Connection[] connections; //连接对象 protected boolean isSupportRange; //是否支持断点下载 protected long timeout; //超时 protected boolean exists; //是否存在 private RandomAccessFile randomAccessFile; protected volatile boolean stop; //停止 private static volatile boolean exception; //是否异常 private AtomicLong prevDownloaded = new AtomicLong(0); //上一次的下载结果 private static Log log = LogFactory.getLog(Downloader.class); private AtomicInteger loseNum = new AtomicInteger(0); private int maxThread; public Downloader(String url, String savePath) throws IOException { //超时一小时 this(url, savePath, 1000 * 60*5,50); } public Downloader(String url, String savePath, long timeout,int maxThread) throws FileNotFoundException { this.timeout = timeout; this.url = url; this.maxThread = maxThread; File file = new File(savePath); if (!file.exists()) file.mkdirs(); this.savePath= file.getAbsolutePath() + "/" + url.substring(url.lastIndexOf("/")); exists = new File(this.savePath).exists(); if(!exists){ randomAccessFile= new RandomAccessFile(this.savePath+".temp", "rw"); channel =randomAccessFile.getChannel(); } } public GetMethod method(long start, long end) throws IOException { GetMethod method = new GetMethod(Downloader.this.url); method.setRequestHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36"); if (end > 0) { method.setRequestHeader("Range", "bytes=" + start + "-" + (end - 1)); } else { method.setRequestHeader("Range", "bytes=" + start + "-"); } HttpClientParams clientParams = new HttpClientParams(); //5秒超时 clientParams.setConnectionManagerTimeout(5000); HttpClient client = new HttpClient(clientParams); client.executeMethod(method); int statusCode = method.getStatusCode(); if (statusCode >= 200 && statusCode < 300) { isSupportRange = (statusCode == 206) ? true : false; } return method; } public void init() throws IOException { size = method(0, -1).getResponseContentLength(); if (isSupportRange) { if (size < 4 * 1024 * 1024) { //假设小于4M connectCount = 1; } else if (size < 10 * 1024 * 1024) { //假设文件小于10M 则两个连接 connectCount = 2; } else if (size < 30 * 1024 * 1024) { //假设文件小于80M 则使用6个连接 connectCount = 3; } else if (size < 60 * 1024 * 1024) { //假设小于60M 则使用10个连接 connectCount = 4; } else { //否则为10个连接 connectCount = 5; } } else { connectCount = 1; } log.debug(String.format("%s size:%s connectCount:%s", this.url, this.size, this.connectCount)); perSize = size / connectCount; connections = new Connection[connectCount]; long offset = 0; for (int i = 0; i < connectCount - 1; i++) { connections[i] = new Connection(offset, offset + perSize); offset += perSize; } connections[connectCount - 1] = new Connection(offset, size); } /** * 强制释放内存映射 * * @param mappedByteBuffer */ static void unmapFileChannel(final MappedByteBuffer mappedByteBuffer) { try { if (mappedByteBuffer == null) { return; } mappedByteBuffer.force(); AccessController.doPrivileged(new PrivilegedAction