多线程下载技术,简单的说就是把要下载的文件分成几块,由不同的线程来负责每一块数据的下载任务。
要使用一个随机访问文件的类:RandomAccessFile类,具体用法请参考:http://blog.youkuaiyun.com/java4found/article/details/8656695
具体思路:
1、文件分块。 文件分块大小block = 文件大小 % 线程数 == 0 ? 文件大小 / 线程数 : 文件大小 / 线程数 + 1 ;
2、确定每一个线程下载对应用文件的位置指针。
现假设为每个线程分别编号threadid 0 1 2 3 4
则第一个线程负责的下载位置是: 0*分块大小 到 (0+1)*分块大小-1
第二个线程负责的下载位置是: 1*分块大小 到 (1+1)*分块大小-1
即有开始下载位置 start = threadid*block;
即有结束下载位置 end = (threadid+1)*block-1;
3、最后通过设置连接的属性, conn.setRequestProperty("Range", "bytes=" + start + "-" + end);
简易的多线程下载代码如下:
import java.io.File;
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 MultiThread {
public static void main(String[] args) {
//文件下载路径
String filePath = "http://dldir1.qq.com/qqfile/qq/QQ2013/QQ2013Beta2.exe";
//文件保存路径
String destination = "D:";
//打算开启的线程数
int threadNum = 5;
new MultiThread().download(filePath, destination, threadNum);
}
/**
* 下载文件
*/
private void download(String filePath, String destination, int threadNum) {
try {
//通过下载路径获取连接
URL url = new URL(filePath);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//设置连接的相关属性
conn.setRequestMethod("GET");
conn.setReadTimeout(5000);
//判断连接是否正确。
if (conn.getResponseCode() == 200) {
// 获取文件大小。
int fileSize = conn.getContentLength();
//得到文件名
String fileName = getFileName(filePath);
//根据文件大小及文件名,创建一个同样大小,同样文件名的文件
File file = new File(destination + File.separator + fileName);
RandomAccessFile raf = new RandomAccessFile(file, "rw");
raf.setLength(fileSize);
raf.close();
// 将文件分成threadNum = 5份。
int block = fileSize % threadNum == 0 ? fileSize / threadNum
: fileSize / threadNum + 1;
for (int threadId = 0; threadId < threadNum; threadId++) {
//传入线程编号,并开始下载。
new DownloadThread(threadId,block, file, url).start();
}
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//由路径获取文件名。
private String getFileName(String filePath) {
return filePath.substring(filePath.lastIndexOf('/') + 1);
}
}
//文件下载线程
class DownloadThread extends Thread {
int start, end,threadId;
File file = null;
URL url = null;
public DownloadThread(int threadId,int block, File file, URL url) {
this.threadId = threadId;
start = block * threadId;
end = block * (threadId + 1) - 1;
this.file = file;
this.url = url;
}
public void run() {
try {
//获取连接并设置相关属性。
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(5000);
//此步骤是关键。
conn.setRequestProperty("Range", "bytes=" + start + "-" + end);
if (conn.getResponseCode() == 206) {
RandomAccessFile raf = new RandomAccessFile(file, "rw");
//移动指针至该线程负责写入数据的位置。
raf.seek(start);
//读取数据并写入
InputStream inStream = conn.getInputStream();
byte[] b = new byte[1024];
int len = 0;
while ((len = inStream.read(b)) != -1) {
raf.write(b, 0, len);
}
System.out.println("线程"+threadId+"下载完毕");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}