多線程下載,以及斷點的實現
阿新 • • 發佈:2019-05-12
popu href 狀態 ktr server tle 找到 row 下載
import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; /** * 多線程下載,以及斷點下載的實現<br> * 當中有個不好的地方,<br> * 就是進度文件的保存的時候假設採用RandomAccessFile的方式進行保存的時候<br> * 盡管會將文件的進度時時的保存在進度文件裏,<br> * 可是,經過實際的測試這樣會大大的減少文件的下載的速度,<br> * 假設採用File和FileOutputStream的話盡管能夠加快下載的速度<br> * 可是進度文件的時時寫入會出現故障.<br> * * <br> * 眼下我還沒有找到非常好的解決方案,假設大家有的話歡迎給我留言. * * @author MartinDong * */ public class Demo { // 定義線程個數 public static int threadCount = 3; // 定義當前存貨的線程個數 public static int runningThread = 3; public static void main(String[] args) throws Exception { // 1,連接到server,獲取一個文件,獲取文件的大小跟server的文件一樣的暫時文件 String path = "http://172.22.64.193:8080/test.exe"; URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); // 設置超時 conn.setConnectTimeout(5000); // 設置請求方式 conn.setRequestMethod("GET"); // 獲取server的返回碼 int code = conn.getResponseCode(); // 推斷返回碼 if (code == 200) { // 獲取返回的長度 int length = conn.getContentLength(); System.out.println("文件總長度:" + length); // 在client創建出一個跟server大小一致的暫時文件 RandomAccessFile raf = new RandomAccessFile("test.exe", "rwd"); // 指定暫時文件的大小 raf.setLength(length); // 釋放資源 raf.close(); // 平均每個線程的文件大小 int blockSize = length / threadCount; for (int threadId = 1; threadId <= threadCount; threadId++) { // 線程開始的下載位置 int startIndex = (threadId - 1) * blockSize; // 線程的結束位置 int endIndex = threadId * blockSize - 1; // 推斷是否是最後一個線程 if (threadId == threadCount) { // 設置結束的位置為到文件的最後 endIndex = length; } System.out.println("線程:" + threadId + "下載:>>>>>>>>" + startIndex + ">>>>>>>>>>" + endIndex); new DownlodeThread(path, threadId, startIndex, endIndex) .start(); } } } /** * 下載文件的子線程類,每個線程下載相應位置文件數據 * * @author MartinDong * */ public static class DownlodeThread extends Thread { private String path; private int threadId; private int startIndex; private int endIndex; /** * * @param path * 文件的下載路徑 * @param threadId * 線程id * @param startIndex * 線程開始的位置 * @param endIndex * 線程結束的位置 */ public DownlodeThread(String path, int threadId, int startIndex, int endIndex) { this.path = path; this.threadId = threadId; this.startIndex = startIndex; this.endIndex = endIndex; } @Override public void run() { try { // 檢查是否存在下載歷史的文件 File tempFile = new File(threadId + ".txt");// =========================斷點記錄操作=============================== if (tempFile.exists() && tempFile.length() > 0) { // 文件輸入流 FileInputStream fis = new FileInputStream( tempFile); // 中間變量,緩存的作用 byte[] tempBuffer = new byte[1024]; // 獲取進度文件的數據大小 int length = fis.read(tempBuffer); // 獲取進度文件的數據 String historyData = new String(tempBuffer, 0, length); // 將進度數據裝換為整型 int historyDataInt = Integer.parseInt(historyData); // 改動真正的下載位置 startIndex = historyDataInt; fis.close(); }// =========================斷點記錄操作=============================== // 將地址轉換為URL URL url = new URL(path); // 獲取http連接 HttpURLConnection conn = (HttpURLConnection) url .openConnection(); // 設置連接的請求方式 conn.setRequestMethod("GET"); // 重要:請求server下載部分的文件,指定文件的位置 conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex); System.out .println("線程:" + threadId + "真實開始的下載進度:" + startIndex); // 設置超時時間 conn.setReadTimeout(5000); // 得到server的狀態碼,200表示請求的所有資源得到響應=== ok,206請求的部分資源得到響應=== ok int code = conn.getResponseCode(); System.out.println("code:" + code); if (code == 206) { // 返回的是指定位置的文件流 InputStream is = conn.getInputStream(); // 創建一個暫時的文件 RandomAccessFile raf = new RandomAccessFile("test.exe", "rwd"); // 移動指針,到指定的文件位置, raf.seek(startIndex); // 創建中間緩沖字節數組 byte[] buffer = new byte[1024]; // 讀取文件的大小 int length = 0; // 定義已經下載的數據長度,用作斷點下載的記錄=========================斷點記錄操作=============================== int downlodeTotal = 0; // 循環寫入 while ((length = is.read(buffer)) != -1) { // 定義一個記錄線程的記錄文件=========================斷點記錄操作=============================== RandomAccessFile historyFile = new RandomAccessFile( threadId + ".txt", "rwd"); // 向文件裏寫入數據 raf.write(buffer, 0, length); // 記錄已經下載的文件長度 downlodeTotal += length; // 將已經下載的文件長度和開始的讀取位置相加,得到已經讀取的文件位置 historyFile.write((downlodeTotal + startIndex + "") .getBytes()); historyFile.close();// =========================斷點記錄操作=============================== System.out.println("線程:" + threadId + "已下載:" + downlodeTotal); } is.close(); raf.close(); System.out.println("線程:" + threadId + "完成下載............"); } else { System.out.println("線程:" + threadId + "下載失敗請又一次下載............"); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { // 進行線程數量的變化操作 runningThread--; // 假設當前存活的線程為0,運行進度文件統一銷毀的操作 if (runningThread == 0) { // 假設完成下載,清除進度文件 for (int threadIndex = 1; threadIndex <= threadCount; threadIndex++) { // 這裏創建的是與線程文件相應的文件,文件名稱能夠自己定義,方便起見是採用的是線程的ID表示 File temp = new File(threadIndex + ".txt"); // 運行文件刪除的操作 temp.delete(); } System.out.println("文件完成下載,刪除進度文件............."); } } } } }
多線程下載,以及斷點的實現