多線程實現多線程下載文件


下載文件的時候,一個大文件切成很多片,用多線程下載,速度會快很多

閱讀代碼的時候注意查看代碼里面的注釋
想用多線程下載文件,則,

第一:得了解  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 (Util.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 就是網速了。

5.調用實例

public static void main(String[] args) {
Date startDate = new Date();

DownloadFilePool pool = new DownloadFilePool("http://fs.w.kugou.com/201809152325/5cbbb70b45431a17cad6ddd6d5342ef5/G108/M03/0C/01/rA0DAFk_VuiALN9DADmXB0zHYTA058.mp3", null, 100);
pool.getFile();

long old = 0;
long now = 0;
while( Util.sum >= Util.start ) {
now = Util.start - old;
old = Util.start;

if(Util.sum == Util.start) {
long t = new Date().getTime() - startDate.getTime();
double speed = ((double)Util.sum / (t/1000.0))/1024.0/1024.0;

System.out.println( "下載完成,用時:" + t/1000.0 +" s 平均網速:" + speed +" M/s" );
break;
}

System.out.println( "網速:" + ((double)(now/0.5))/1024.0/1024.0 +" M/s,已完成:" + (Util.start / (double)Util.sum)*100 +"%" );

try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
 

運行效果:
---------------------
作者:臨窗,聽雨聲
來源:CSDN
原文:https://blog.csdn.net/yali_aini/article/details/82713368
版權聲明:本文為博主原創文章,轉載請附上博文鏈接!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM