多线程下载
多线程下载技术,简单的说就是把要下载的文件分成几块,由不同的线程来负责每一块数据的下载任务。
技术要点
- RandomAccessFile:
Java中用来实现随机访问文件的类 - http Range请求头
具体思路
1、文件分块。 文件分块大小(blockSize)= (文件大小 +线程数 - 1 )/ 线程数 ;
2、确定每一个线程所要下载的 文件的起始和结束位置。
现假设为每个线程分别编号:0,1, 2,3;则
第一个线程负责的下载位置是: 0*blockSize - (0+1)*blockSize -1,
第二个线程负责的下载位置是: 1*blockSize - (1+1)*blockSize -1,
以此类推第i个线程负责的下载位置是:i*blockSize - (i+1)*blockSize -1;
即线程(编号为id)下载开始位置 start = id*block;
即线程(编号为id)下载结束位置 end = (id+1)*block -1;
3、设置http 请求头, conn.setRequestProperty(“Range”, “bytes=” + start + “-” + end);
代码实现
一个简单的Java多线程下载代码如下:
package com.ricky.java.test.download;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
public class Downloader {
private URL url; // 目标地址
private File file; // 本地文件
private static final int THREAD_AMOUNT = 8; // 线程数
private static final String DOWNLOAD_DIR_PATH = "D:/Download"; // 下载目录
private int threadLen; // 每个线程下载多少
public Downloader(String address, String filename) throws IOException { // 通过构造函数传入下载地址
url = new URL(address);
File dir = new File(DOWNLOAD_DIR_PATH);
if(!dir.exists()){
dir.mkdirs();
}
file = new File(dir, filename);
}
public void download() throws IOException {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
int totalLen = conn.getContentLength(); // 获取文件长度
threadLen = (totalLen + THREAD_AMOUNT - 1) / THREAD_AMOUNT; // 计算每个线程要下载的长度
System.out.println("totalLen="+totalLen+",threadLen:"+threadLen);
RandomAccessFile raf = new RandomAccessFile(file, "rws"); // 在本地创建一个和服务端大小相同的文件
raf.setLength(totalLen); // 设置文件的大小
raf.close();
for (int i = 0; i < THREAD_AMOUNT; i++) // 开启3条线程, 每个线程下载一部分数据到本地文件中
new DownloadThread(i).start();
}
private class DownloadThread extends Thread {
private int id;
public DownloadThread(int id) {
this.id = id;
}
public void run() {
int start = id * threadLen; // 起始位置
int end = id * threadLen + threadLen - 1; // 结束位置
System.out.println("线程" + id + ": " + start + "-" + end);
try {
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(5000);
conn.setRequestProperty("Range", "bytes=" + start + "-" + end); // 设置当前线程下载的范围
InputStream in = conn.getInputStream();
RandomAccessFile raf = new RandomAccessFile(file, "rws");
raf.seek(start); // 设置保存数据的位置
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1)
raf.write(buffer, 0, len);
raf.close();
System.out.println("线程" + id + "下载完毕");
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) throws IOException {
String address = "http://dldir1.qq.com/qqfile/qq/QQ7.9/16621/QQ7.9.exe";
new Downloader(address, "QQ7.9.exe").download();
// String address = "http://api.t.dianping.com/n/api.xml?cityId=2";
// new Downloader(address, "2.xml").download();
}
}
封装多线程下载
文件下载是一个常用的模块,我们可以对其封装一下,方便以后调用。涉及到的开发技术如下:
- JDK 1.7
- Eclipse Juno
- Maven 3
- HttpClient 4.3.6
工程目录结构如下所示:
com.ricky.common.java.download.FileDownloader
package com.ricky.common.java.download;
import org.apache.log4j.Logger;
import com.ricky.common.java.download.config.FileDownloaderConfiguration;
/**
* Java 文件多线程下载
* @author Ricky Fung
*
*/
public class FileDownloader {
protected Logger mLogger = Logger.getLogger("devLog");
private volatile static FileDownloader fileDownloader;
private FileDownloaderEngine downloaderEngine;
private FileDownloaderConfiguration configuration;
public static FileDownloader getInstance(){
if(fileDownloader==null){
synchronized (FileDownloader.class) {
if(fileDownloader==null){
fileDownloader = new FileDownloader();
}
}
}
return