使用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