412
技術社區[雲棲]
使用Apache HttpClient實現多線程下載的小例子
網上類似的文章很多,參考了很多人的,大部分人都是用URLConnection寫的。
原理一:HTTP多線程下載原理
1、發送一個含有Rang頭的Head請求,如果返回狀態碼為206,則允許多線程下載
原理二:多線程下載原理
1、使用HttpClient的Head請求獲取請求文件的信息
2、發送一個Rang的Head請求判斷是否允許多線程下載
3、通過主任務創建多個分段下載線程,分段下載文件,然後用Java的隨機讀寫文件類保存下載的內容
(等有時間了再添加內容吧,先簡單寫這麼多)
調度功能代碼片段
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);
- }
/**
* 開始下載
* @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();
- }
- }
/**
* 現在過程代碼
*/
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、運行截圖
- Download.jar (928.1 KB)
- 下載次數: 44
- DownLoad.zip (832.5 KB)
- 下載次數: 70
最後更新:2017-04-02 06:51:46
