使用Apache HttpClient實現多執行緒下載的小例子
阿新 • • 發佈:2019-01-24
網上類似的文章很多,參考了很多人的,大部分人都是用URLConnection寫的。
原理一:HTTP多執行緒下載原理
1、傳送一個含有Rang頭的Head請求,如果返回狀態碼為206,則允許多執行緒下載
原理二:多執行緒下載原理
1、使用HttpClient的Head請求獲取請求檔案的資訊
2、傳送一個Rang的Head請求判斷是否允許多執行緒下載
3、通過主任務建立多個分段下載執行緒,分段下載檔案,然後用Java的隨機讀寫檔案類儲存下載的內容
(等有時間了再新增內容吧,先簡單寫這麼多)
排程功能程式碼片段
Java程式碼- /**
- * 開始下載
- * @throws Exception
- */
- publicvoid startDown() throws Exception{
- HttpClient httpClient = new DefaultHttpClient();
- try {
- //獲取下載檔案資訊
- getDownloadFileInfo(httpClient);
- //啟動多個下載執行緒
- startDownloadThread();
- //開始監視下載資料
- monitor();
- } catch (Exception e) {
- throw e;
- } finally {
- httpClient.getConnectionManager().shutdown();
- }
- }
- /**
- * 獲取下載檔案資訊
- */
- privatevoid 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) thrownew 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
- */
- privatevoid 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() {
- publicvoid afterPerDown(DownloadThreadEvent event) {
- //下載完一個片段後追加已下載位元組數
- synchronized (object) {
- DownloadTask.this.receivedCount += event.getCount();
- }
- }
- publicvoid 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
- */
- publicvoid startDown() throws Exception{
- HttpClient httpClient = new DefaultHttpClient();
- try {
- //獲取下載檔案資訊
- getDownloadFileInfo(httpClient);
- //啟動多個下載執行緒
- startDownloadThread();
- //開始監視下載資料
- monitor();
- } catch (Exception e) {
- throw e;
- } finally {
- httpClient.getConnectionManager().shutdown();
- }
- }
- /**
- * 獲取下載檔案資訊
- */
- privatevoid 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) thrownew 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
- */
- privatevoid 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() {
- publicvoid afterPerDown(DownloadThreadEvent event) {
- //下載完一個片段後追加已下載位元組數
- synchronized (object) {
- DownloadTask.this.receivedCount += event.getCount();
- }
- }
- publicvoid 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);
}
分段下載執行緒程式碼片段:
Java程式碼- /**
- * 現在過程程式碼
- */
- publicvoid 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=newbyte[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();
- }
- }
- /**
- * 現在過程程式碼
- */
- publicvoid 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=newbyte[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、執行截圖