/**
* 多线程下载工具类:
* 如果要实现断点下载,要额外增加一个配置文件
* 所有的断点下载工具都会在下载开始时生成两个文件:一个是与网络资源具有相同大小的空文件,一个是配置文件
* 该配置文件分别记录每个线程已经下载到哪个字节,当网络断开再次开始下载时,每个线程根据配置文件里记录的位置向后下载即可
*
* 程序中DownUtils类中的download()方法负责按如下步骤来实现多线程下载
* 1.创建URL对象
* 2.获取指定URL对象所指向的资源大小,通过URLConnection类中getConnectLength()方法获得
* 3.在本地磁盘上创建一个与网络资源具有相同大小的空文件
* 4.计算每个线程应该下载网络资源的哪个部分(从哪个字节开始,到哪个字节结束)
* 5.依次创建、启动多个线程来下载网络资源的指定部分
*/
package codes17;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
public class DownUtil {
private String path; // 定义下载资源的路径
private String targetFile; // 指定所下载的文件的保存位置
private int threadNum; // 定义需要使用多少个线程下载资源
private DownThread[] threads; // 定义下载的线程对象
private int fileSize; // 定义下载的文件的总大小
public DownUtil(String path, String targetFile, int threadNum) {
super();
this.path = path;
this.targetFile = targetFile;
this.threadNum = threadNum;
threads = new DownThread[threadNum];
}
public void download() {
URL url;
HttpURLConnection conn = null;
RandomAccessFile file = null;
try {
url = new URL(path);
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5 * 1000);
conn.setRequestMethod("GET");
conn.setRequestProperty(
"Accept",
"image/gif,image/jpeg,image/pjpeg,"
+ "application/x-shockwave-flash,application/xaml+xml,"
+ "application/vnd.ms-xpsdocument,application/x-ms-xbap,"
+ "application/x-ms-application, application/vnd.ms-excel,"
+ "application/vnd.ms-powerpoint,application/msword, */*");
conn.setRequestProperty("Accept-Language", "zh-CH");
conn.setRequestProperty("Charset", "UTF-8");
conn.setRequestProperty("Connection", "Keep-Alive");
fileSize = conn.getContentLength(); // 得到文件大小
conn.disconnect();
int currentPartSize = fileSize / threadNum + 1;
file = new RandomAccessFile(targetFile, "rw");
file.setLength(fileSize); // 设置本地文件大小
for (int i = 0; i < threadNum; i++) {
int startPos = i * currentPartSize; // 计算每个线程下载的开始位置
RandomAccessFile currentPart = new RandomAccessFile(targetFile,
"rw"); // 每个线程使用一个RandAccessFile进行下载
currentPart.seek(startPos); // 定位该线程的下载位置
threads[i] = new DownThread(startPos, currentPartSize,
currentPart); // 创建下载线程
threads[i].start();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (file != null) {
try {
file.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 获取下载的完成百分比
*
* @return 返回已经完成的百分比
*/
public double getCompleteRate() {
// 统计多个线程已经下载的总大小
int sumSize = 0;
for (int i = 0; i < threadNum; i++) {
sumSize += threads[i].length;
}
return sumSize * 1.0 / fileSize;
}
private class DownThread extends Thread {
private int startPos; // 当前线程的下载位置
private int currentPartSize; // 定义当前线程负责下载的文件大小
private RandomAccessFile currentPart; // 当前线程需要下载的文件块
public int length; // 定义该线程已下载的字数
public DownThread(int startPos, int currentPartSize,
RandomAccessFile currentPart) {
this.startPos = startPos;
this.currentPartSize = currentPartSize;
this.currentPart = currentPart;
}
public void run() {
HttpURLConnection conn = null;
InputStream inStream = null;
try {
URL url = new URL(path);
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5 * 1000);
conn.setRequestMethod("GET");
conn.setRequestProperty(
"Accept",
"image/gif,image/jpeg,image/pjpeg,"
+ "application/x-shockwave-flash,application/xaml+xml,"
+ "application/vnd.ms-xpsdocument,application/x-ms-xbap,"
+ "application/x-ms-application, application/vnd.ms-excel,"
+ "application/vnd.ms-powerpoint,application/msword, */*");
conn.setRequestProperty("Accept-Language", "zh-CH");
conn.setRequestProperty("Charset", "UTF-8");
inStream = conn.getInputStream();
inStream.skip(startPos); // 跳过startPos个字节,表明该线程只下载自己负责的那部分文件
byte[] buffer = new byte[1024];
int hasRead = 0;
// 读取网络数据,并写入本地文件
while (length < currentPartSize
&& (hasRead = inStream.read(buffer)) != -1) {
currentPart.write(buffer, 0, hasRead);
length += hasRead; // 累计该线程下载的总大小
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (currentPart != null)
currentPart.close();
if (inStream != null)
inStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
主程序
package codes17;
public class MultiThreadDown {
public static void main(String[] args) {
// 初始化DownUtil 对象
final DownUtil downUtil = new DownUtil(
"http://softdownload.hao123.com/hao123-soft-online-bcs/soft/Y/2013-07-18_YoudaoDict_baidu.alading.exe",
"YoudaoDict_baidu.alading.exe", 10);
downUtil.download(); // 开始下载
new Thread() {
public void run() {
while (downUtil.getCompleteRate() < 1) {
// 每个0.1秒查询一次任务的完成进度,GUI程序中可根据该进度来绘制进度条
System.out.println("已下载:" + downUtil.getCompleteRate()
* 100 + "%");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
} while (downUtil.getCompleteRate() >= 1) {
System.out.println("下载完成");
break;
}
}
}.start();
}
}
输出结果:
已下载:0.0%
已下载:19.772402605784702%
已下载:30.009086110800354%
已下载:37.48737840962224%
已下载:40.051425809431194%
已下载:44.387157244936304%
已下载:53.9980501077138%
已下载:80.66207412889513%
下载完成