|
网上类似的文章很多,参考了很多人的,大部分人都是用URLConnection写的。
原理一:HTTP多线程下载原理
1、发送一个含有Rang头的Head请求,如果返回状态码为206,则允许多线程下载
原理二:多线程下载原理
1、使用HttpClient的Head请求获取请求文件的信息
2、发送一个Rang的Head请求判断是否允许多线程下载
3、通过主任务创建多个分段下载线程,分段下载文件,然后用Java的随机读写文件类保存下载的内容
(等有时间了再添加内容吧,先简单写这么多)
调度功能代码片段
/**
* 开始下载
* @throws Exception
*/
public void startDown() throws Exception{
HttpClient httpClient = new DefaultHttpClient();
try {
//获取下载文件信息
getDownloadFileInfo(httpClient);
//启动多个下载线程
startDownloadThread();
//开始监视下载数据
monitor();
} catch (Exception e) {
throw e;
} finally {
httpClient.getConnectionManager().shutdown();
}
}
/**
* 获取下载文件信息
*/
private void getDownloadFileInfo(HttpClient httpClient) throws IOException,
ClientProtocolException, Exception {
HttpHead httpHead = new HttpHead(url);
HttpResponse response = httpClient.execute(httpHead);
//获取HTTP状态码
int statusCode = response.getStatusLine().getStatusCode();
if(statusCode != 200) throw new Exception("资源不存在!");
if(getDebug()){
for(Header header : response.getAllHeaders()){
System.out.println(header.getName()+":"+header.getValue());
}
}
//Content-Length
Header[] headers = response.getHeaders("Content-Length");
if(headers.length > 0)
contentLength = Long.valueOf(headers[0].getValue());
httpHead.abort();
httpHead = new HttpHead(url);
httpHead.addHeader("Range", "bytes=0-"+(contentLength-1));
response = httpClient.execute(httpHead);
if(response.getStatusLine().getStatusCode() == 206){
acceptRanges = true;
}
httpHead.abort();
}
/**
* 启动多个下载线程
* @throws IOException
* @throws FileNotFoundException
*/
private void startDownloadThread() throws IOException,
FileNotFoundException {
//创建下载文件
File file = new File(localPath);
file.createNewFile();
RandomAccessFile raf = new RandomAccessFile(file, "rw");
raf.setLength(contentLength);
raf.close();
//定义下载线程事件实现类
DownloadThreadListener listener = new DownloadThreadListener() {
public void afterPerDown(DownloadThreadEvent event) {
//下载完一个片段后追加已下载字节数
synchronized (object) {
DownloadTask.this.receivedCount += event.getCount();
}
}
public void downCompleted(DownloadThreadEvent event) {
//下载线程执行完毕后从主任务中移除
threads.remove(event.getTarget());
if(getDebug()){
System.out.println("剩余线程数:"+threads.size());
}
}
};
//不支持多线程下载时
if (!acceptRanges) {
if(getDebug()){
System.out.println("该地址不支持多线程下载");
}
//定义普通下载
DownloadThread thread = new DownloadThread(url, 0, contentLength, file, false);
thread.addDownloadListener(listener);
thread.start();
threads.add(thread);
return;
}
//每个请求的大小
long perThreadLength = contentLength / threadCount + 1;
long startPosition = 0;
long endPosition = perThreadLength;
//循环创建多个下载线程
do{
if(endPosition >= contentLength)
endPosition = contentLength - 1;
DownloadThread thread = new DownloadThread(url, startPosition, endPosition, file);
thread.addDownloadListener(listener);
thread.start();
threads.add(thread);
startPosition = endPosition + 1;//此处加 1,从结束位置的下一个地方开始请求
endPosition += perThreadLength;
} while (startPosition < contentLength);
}
分段下载线程代码片段:
/**
* 现在过程代码
*/
public void run() {
if(DownloadTask.getDebug()){
System.out.println("Start:" + startPosition + "-" +endPosition);
}
HttpClient httpClient = new DefaultHttpClient();
try {
HttpGet httpGet = new HttpGet(url);
if(isRange){//多线程下载
httpGet.addHeader("Range", "bytes="+startPosition+"-"+endPosition);
}
HttpResponse response = httpClient.execute(httpGet);
int statusCode = response.getStatusLine().getStatusCode();
if(DownloadTask.getDebug()){
for(Header header : response.getAllHeaders()){
System.out.println(header.getName()+":"+header.getValue());
}
System.out.println("statusCode:" + statusCode);
}
if(statusCode == 206 || (statusCode == 200 && !isRange)){
InputStream inputStream = response.getEntity().getContent();
//创建随机读写类
RandomAccessFile outputStream = new RandomAccessFile(file, "rw");
//跳到指定位置
outputStream.seek(startPosition);
int count = 0;byte[] buffer=new byte[1024];
while((count = inputStream.read(buffer, 0, buffer.length))>0){
outputStream.write(buffer, 0, count);
//触发下载事件
fireAfterPerDown(new DownloadThreadEvent(this,count));
}
outputStream.close();
}
httpGet.abort();
} catch (Exception e) {
e.printStackTrace();
} finally {
//触发下载完成事件
fireDownCompleted(new DownloadThreadEvent(this, endPosition));
if(DownloadTask.getDebug()){
System.out.println("End:" + startPosition + "-" +endPosition);
}
httpClient.getConnectionManager().shutdown();
}
}
附件说明:
1、Download.jar为编译好的可运行程序
2、Download.zip为Eclipse项目文件
3、运行截图
|
|
|