import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
public class Main {
// 設置線程的數量爲3
private final static int THRED_COUNT = 3;
final static String path = "http://188.188.7.85/kugou.exe";
public static void main(String[] args) {
System.out.println(getFilename());
// 創建三個線程進行多線程的下載資源文件
try {
URL url = new URL(path);
// 1.獲取文件資源的連接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
// 2.獲取服務器資源文件的大小
int length = conn.getContentLength();
//3.在本地生成與之一樣的文件,使用隨機流
RandomAccessFile raf = new RandomAccessFile(getFilename(), "rw");
//設置文件的長度
raf.setLength(length);
//4.開始劃分每一個線程從什麼位置下載到什麼位置
int blockSize = length / THRED_COUNT;
//5.開啓3個線程進行下載
for (int threadId = 0; threadId < THRED_COUNT; threadId++) {
//每個線程的下載開始位置
int startIndex = threadId*blockSize;
//線程下載的結束位置
int endIndex = (threadId + 1 )*blockSize -1;
//如果是最後一個線程,讓這個線程執行後面剩餘的文字資源
if(threadId == THRED_COUNT){
endIndex = length - 1;
}
//開始線程去下載,並傳遞進去每一個線程下載的起始位置
new Downloader(threadId,startIndex,endIndex).start();
}
} catch (Exception e) {
e.printStackTrace();
}
}
//獲取路徑中的文件名
private static String getFilename(){
return path.substring(path.lastIndexOf("/")+1);
}
//下載的線程
static class Downloader extends Thread{
private int startIndex;
private int endIndex;
private int threadId;
private int currentPosition;
public Downloader(int threadId,int startIndex,int endIndex) {
this.startIndex = startIndex;
this.endIndex = endIndex;
this.threadId = threadId;
currentPosition = startIndex;
System.out.println("線程"+threadId+"-下載的位置從:" + startIndex+" ---"+endIndex);
}
@Override
public void run() {
try{
//先找到之前記錄好的位置信息
File file = new File(threadId+".position");
//判斷以前曾經下載過文件,這個時候必須讀取裏裏面的位置信息
//(斷點下載的方式:就是記錄每次下載之後的位置,並實時保存到硬盤中,所以使用到了隨機流進行實時寫出斷點下載的位置文件信息)
if(file.exists() && file.length()>0){
FileInputStream fis = new FileInputStream(file);
BufferedReader bReader = new BufferedReader(new InputStreamReader(fis));
//讀取線程曾經下載到的位置
currentPosition = Integer.parseInt(bReader.readLine());
System.out.println("以前有下載過文件--"+threadId +"--從"+currentPosition+"開始下載");
bReader.close();
fis.close();
}else{
System.out.println("以前沒有下載過文件,從頭開始下載------");
}
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//conn.getInputStream()//獲取這個資源對應的所有輸入流
//爲了實現每一線程下載的東西都是固有的位置,所以必須告訴服務器,每一個線程從什麼位置開始下載
//設置線程下載資源的位置
conn.setRequestProperty("Range", "bytes:"+currentPosition+"-"+endIndex);
//獲取線程訪問返回的狀態碼 線程下載返回的是206
int code = conn.getResponseCode();
if(206 == code){
InputStream in = conn.getInputStream();
//寫文件的時候要注意,因爲已經規定了每一個線程從什麼位置開始下載,所以
//寫數據的時候必須從指定位置開始寫入
RandomAccessFile raf = new RandomAccessFile(getFilename(), "rw");
raf.seek(currentPosition);
byte[] buffer = new byte[1024];
int len = 0;
while((len = in.read(buffer)) != -1){
raf.write(buffer,0,len);
//這幾必須寫在上面這句語句後面,不然會數據混亂問題出現問題
currentPosition += len;
//必須使用隨機流進行讀寫現在下載位置的配置信息,不然會出現各種問題,(主要是硬盤的緩存問題)
RandomAccessFile fos= new RandomAccessFile(threadId+".position", "rwd");
fos.write((currentPosition+"").getBytes());
fos.close();
}
System.out.println("線程"+threadId+"--已經下載結束了。。");
//下載完成之後,要刪除(斷點下載的配置文件.position)
//如果存在斷點配置.position文件,將其刪除
file.delete();
raf.close();
in.close();
}
}catch (Exception e) {
e.printStackTrace();
}
}
}
}
一.注意:多線程下載資源文件是爲了提高效率(比如:迅雷下載)
斷點下載的原理:每次讀取到的文件都將其的位置寫入配置文件中
下面是一個Demo: