java線程實戰:多線程下載(下) 轉

多線程下載業務邏輯:

1、URL請求獲取下載文件的大小、計算每個線程下載的起始位置

2、RandomAccessFile類在存儲空間佔位,隨機訪問流在多線程下可同時讀寫文件

3、開啓線程,每個線程負責下載文件的一部分,最後由隨機訪問流自動拼接文件,合成

4、啓動線程

此項目需要了解java.io.RandomAccessFile、java.net .HttpURLConnection 的具體用法,在線API參考文檔【http://tool.oschina.net/apidocs/apidoc?api=jdk-zh】

 

java.lang.Object
  java.net.URLConnection
      java.net.HttpURLConnection

重點:HttpURLConnection的父類URLConnection有個非常重要的 setRequestProperty() 方法,以下鏈接介紹了有關多線程下載,Range請求頭的使用例子。

 

詳情點擊:【http://www.tuicool.com/articles/viUfMjY】

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
 
public class Demo {
	//定義全局變量address
	static String address = "http://localhost:8080/ThreadDownloadServer/Blood.mp4";
	
	public static void main(String[] args) {
		//開啓三個線程下載
		downloadFile(3);
	}
	
	/**
	 * 功能:計算每個線程下載的起始位置,並執行下載。
	 * @param 線程數
	 */
	public static void downloadFile(int threadCount){
		
		try {
			URL url = new URL(address);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setConnectTimeout(5000);
			conn.setReadTimeout(5000);
			//獲取文件字節總大小
			int fileLength = conn.getContentLength();
			System.out.println("文件總大小:"+fileLength/1024/1024+"MB");
			int code = conn.getResponseCode();
			if (code == 200) {
				//分配每個線程"平均"下載字節數
				int blockSize = fileLength/threadCount;
				//創建隨機訪問流,在本地爲文件佔位
				RandomAccessFile raf = new RandomAccessFile("Blood.mp4", "rw");
				//重點:假設文件只有10字節,分3個線程來下載,理解理解for循環代碼
				for (int threadId = 0; threadId < threadCount; threadId++) {
					int startIndex = threadId*blockSize;
					int endIndex = (threadCount+1)*blockSize-1;
					if (threadId == threadCount) {
						endIndex = fileLength - 1;
					}
					//每循環一次,開啓一個線程執行下載,別漏了start()。
					new ThreadDownload(threadId, startIndex, endIndex).start();;
				}
				conn.disconnect();
			}else{
				System.out.println("請求失敗,服務器響應碼:"+code);
			}
		} catch (MalformedURLException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	
	/**
	 * 功能:開啓線程下載,下載分配好的文件長度
	 * @author Administrator
	 * @param 線程ID,下載開始位置,結束位置,
	 */
	public static class ThreadDownload extends Thread{
		private int threadId;
		private int startIndex;
		private int endIndex;
		
		//構造方法接收3個參數初始化局部變量:線程ID、開始位置、結束位置
		public ThreadDownload(int threadId, int startIndex, int endIndex) {
			this.threadId = threadId;
			this.startIndex = startIndex;
			this.endIndex = endIndex;	
		}
	
		@Override
		public void run(){
			try {
				URL url = new URL(address);
				HttpURLConnection conn = (HttpURLConnection) url.openConnection();
				conn.setConnectTimeout(5000);
				conn.setReadTimeout(5000);
				System.out.println("線程"+threadId+"下載開始位置"+startIndex+" 結束位置:"+endIndex);
				
				//設置請求頭,請求部分資源下載
				conn.setRequestProperty("Range", "bytes:"+startIndex+"-"+endIndex);
				//服務器響應碼206表示請求部分資源成功
				if (conn.getResponseCode()==206) {
					InputStream inputStream = conn.getInputStream();
					RandomAccessFile raf = new RandomAccessFile(new File("Blood.mp4"), "rw");
					//查找寫入開始位置
					raf.seek(startIndex);
					byte array[] = new byte[1024];
					int len = -1;
					while((len = inputStream.read(array))!=-1){
						raf.write(array, 0, len);
					}
					inputStream.close();
					raf.close();
					conn.disconnect();
				}else{
					System.out.println("服務器響應碼:"+conn.getResponseCode());
				}
			} catch (MalformedURLException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

刷新項目,可見項目已生成:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章