多線程下載業務邏輯:
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();
}
}
}
}
刷新項目,可見項目已生成: