1. 程式人生 > >java使用 網路連線+RandomAccessFile + io 多執行緒實現多執行緒下載檔案並顯示實時網速

java使用 網路連線+RandomAccessFile + io 多執行緒實現多執行緒下載檔案並顯示實時網速

下載檔案的時候,一個大檔案切成很多片,用多執行緒下載,速度會快很多

閱讀程式碼的時候注意檢視程式碼裡面的註釋

想用多執行緒下載檔案,則,

第一:得了解  RandomAccessFile  類,這是個隨機訪問檔案類,裡面可以設定 訪問的 開始地址和結束地址,且該類可讀可寫。

RandomAccessFile out = new RandomAccessFile(file, "rw"); 則表示,該類可讀可寫。通過 out.seek(start)  可以定位開始讀取的位置。

第二:既然是網路檔案下載,那就必須得了解 URL 類,該類是 java.net 包提供的一個 可以用來網路連線的類。

URL url = new URL(urlLocation); 可以這樣例項化該類,然後開啟連線,HttpURLConnection conn = (HttpURLConnection) url.openConnection();,還可以設定些別的引數,比如說設定超時,設定訪問方法,設定 訪問 起始點之類的。

        conn.setConnectTimeout(5000);         conn.setRequestMethod("GET");

        conn.setRequestProperty("Range", "bytes=" + start +"-"+end );

第三:瞭解執行緒,這裡我們使用 java 1.5之後引入的 concurrent 包裡面的  Executors.newCachedThreadPool() 執行緒池

第四:最基本的 io讀寫得知道

看看程式碼吧。

1.為了方便,我寫了個工具類,用於提供 Util類用來提供獲取 HttpURLConnection 連線

public class Util {

	// 記錄讀取了多少,一共讀取了多少
	public static long start;
	// 記錄檔案總大小
	public static long sum;

	/**
	 * 
	 * @Title: getHttpConnection   
	 * @Description: 獲取 url 連線
	 * @param: @param urlLocation
	 * @param: @return HttpURLConnection例項化物件
	 * @param: @throws IOException      
	 * @return: HttpURLConnection      
	 * @throws
	 */
	public static HttpURLConnection getHttpConnection(String urlLocation) throws IOException {
		URL url = new URL(urlLocation);
		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
		conn.setConnectTimeout(5000);
		conn.setRequestMethod("GET");

		return conn;
	}
}

2.檔案切片,分別指定 起始點,注意這裡的起始點是包頭也包尾的,0-10/11-20/21-30  這種

public class DownloadFilePool {
	// 網路資源路徑
	private String urlLocation;
	// 儲存路徑
	private String filePath;
	// 多少個執行緒
	private int poolLength;
	
	public DownloadFilePool(String urlLocation, String filePath, int poolLength) {
		super();
		// 如果 儲存路徑為空則預設存在 D盤,檔名跟下載名相同
		if( filePath==null ) {
			String fileName = urlLocation.substring( urlLocation.lastIndexOf("/") +1);
			filePath = "D:/" + fileName;
		}
		
		this.urlLocation = urlLocation;
		this.filePath = filePath;
		this.poolLength = poolLength;
	}
	
	public void getFile() {
		try {
			// 獲取檔案長度
			long fileLength = Util.getHttpConnection(urlLocation).getContentLengthLong();
			Util.sum = fileLength;
			
			ExecutorService pool = Executors.newCachedThreadPool();
			
			// 獲取每片大小
			long slice = fileLength/poolLength;
			for(int i = 0 ;i < poolLength; i++) {
				long start = i*slice;
	            long end = (i+1)*slice -1;
				
				if(i==poolLength-1) {
					start = i*slice;
					end =  fileLength ;
				}
				System.out.println(  start + "---" + end  );
				// 建立下載類
				DownloadFileRang downloadFileRang = new DownloadFileRang(start, end, urlLocation, filePath);
				// 執行執行緒
				pool.execute(downloadFileRang);
			}
			// 關閉執行緒池
			pool.shutdown();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

3.檔案下載類,此處使用 繼承 Runnable 實現多執行緒

public class DownloadFileRang implements Runnable {
	// 檔案開始位置
	private long start ;
	// 檔案結束位置
	private long end;
	// url地址
	private String urlLocation;
	// 檔案儲存位置
	private String filePath;
	
	public DownloadFileRang(long start, long end, String urlLocation, String filePath) {
		super();
		this.start = start;
		this.end = end;
		this.urlLocation = urlLocation;
		this.filePath = filePath;
	}

	@Override
	public void run() {
		try {
			// 獲取連線
			HttpURLConnection conn = Util.getHttpConnection(urlLocation);
			// 設定獲取資源範圍
			conn.setRequestProperty("Range", "bytes=" + start +"-"+end );
			
			File file = new File(filePath);
			RandomAccessFile out = null;
			if(file!=null) {
				 out = new RandomAccessFile(file, "rw");
			}
			out.seek(start);
			
			// 獲取網路連線的 輸入流
			InputStream is = conn.getInputStream();
			
			byte [] data = new byte[1024];
			int len = 0;
			while( (len = is.read(data))!=-1 ) {
				out.write(data, 0, len);
				synchronized (DownLoadFile.class) {
					Util.start += len;
				}
			}
			
			// 關閉連線
			out.close();
			is.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

4.實現顯示實時網速

實時網速,其實就是單位時間內,所讀取到的 資源,我們這裡就是讀取到的 len,觀察我的程式碼,可以發現,我在 Util類裡面寫了兩個靜態屬性,分別是 start 用來記錄一共讀了多少,一個是 sum,記錄總檔案大小。

執行緒類裡面的 out.write 方法裡面我 用 start 進行累加 len,這就是記錄一共讀取了多少資源,因為 多執行緒不能保證資料的原子性,所以我這裡累加的時候,為避免因為執行緒原因出現數據錯誤,則進行加鎖,加上  Util 物件的鎖,告訴別的執行緒,這個 strat 同意時刻內只能一個執行緒來進行 操作。然後  用 每個時間段,比如說我這裡是 500ms ,內讀取的資源 / 500ms 就是網速了。