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 就是網速了。