標籤:下載 模式 多線程 斷點
RandomAccessFile類:
此類的執行個體支援對隨機訪問檔案的讀取和寫入。隨機訪問檔案的行為類似儲存在檔案系統中的一個大型 byte 數組。存在指向該隱含數組,游標或索引,稱為檔案指標;輸入操作從檔案指標開始讀取位元組,並隨著對位元組的讀取而前移此檔案指標。如果隨機訪問檔案以讀取/寫入模式建立,則輸出操作也可用;輸出操作從檔案指標開始寫入位元組,並隨著對位元組的寫入而前移此檔案指標。寫入隱含數組的當前末尾之後的輸出操作導致該數組擴充。該檔案指標可以通過 getFilePointer 方法讀取,並通過 seek 方法設定。
下面有RandomAccessFile實現安卓下的斷點下載的demo。
伺服器端可以用tomcat類比,將被下載的測試檔案放入webApp/ROOT目錄下即可。
先給出java藉助HttpURLConnection類實現的多線程下載代碼:
public class MultiThread { private static int threadCount = 3; private static long blockSize; private static int runningThreadCount; public static void main(String[] args) throws Exception { String path = "http://10.0.67.172/test.exe"; URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000);//逾時時間 int code = conn.getResponseCode(); System.out.println(code); if(code / 100 == 2){ int size = conn.getContentLength();//擷取資源檔的長度 System.out.println("請求資源大小:" + size); blockSize = size / threadCount;//將資源檔分為多少塊,沒一塊的大小 runningThreadCount = threadCount; long startIndex = 0; long endIndex = 0; //開啟若干個子線程去實現多線程的下載 for(int i = 0; i < threadCount; i++){ startIndex = i * blockSize; endIndex = (i + 1) * blockSize - 1; if(i == threadCount-1){ endIndex = size - 1; } System.out.println("開啟線程:" + i + ";" + "開始位置:" + startIndex + ":" + "結束位置:" + endIndex); new DownThread(path, startIndex, endIndex, i).start(); } } } private static class DownThread extends Thread{ private String path; private long startIndex; private long endIndex; private int threadId; public DownThread(String path, long startIndex, long endIndex, int threadId) { super(); this.path = path; this.startIndex = startIndex; this.endIndex = endIndex; this.threadId = threadId; } @Override public void run() { try { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setReadTimeout(5000); conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);//設定伺服器上的檔案的讀取位置 int code = conn.getResponseCode(); if(code / 100 == 2){ InputStream is = conn.getInputStream(); File file = new File("temp.exe"); RandomAccessFile raf = new RandomAccessFile(file, "rw"); raf.seek(startIndex); System.out.println("第" + threadId + "個檔案的開始位置:" + String.valueOf(startIndex)); int len = 0; byte[] buffer = new byte[1024]; while ((len = is.read(buffer)) != -1){ raf.write(buffer, 0, len);//寫檔案 } raf.close(); } } catch (Exception e) { e.printStackTrace(); } } }}
斷點下載的原理就是將上次檔案下載的位置儲存為臨時檔案,當完全下載完畢時再刪除。
public class MultiThread { private static int threadCount = 3; private static long blockSize; private static int runningThreadCount; public static void main(String[] args) throws Exception { String path = "http://10.0.67.172/test.rar"; URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setConnectTimeout(5000);//逾時時間 int code = conn.getResponseCode(); System.out.println(code); if(code / 100 == 2){ int size = conn.getContentLength();//擷取資源檔的長度 System.out.println("請求資源大小:" + size); blockSize = size / threadCount;//將資源檔分為多少塊,沒一塊的大小 runningThreadCount = threadCount; long startIndex = 0; long endIndex = 0; for(int i = 0; i < threadCount; i++){ startIndex = i * blockSize; endIndex = (i + 1) * blockSize - 1; if(i == threadCount-1){ endIndex = size - 1; } System.out.println("開啟線程:" + i + ";" + "開始位置:" + startIndex + ":" + "結束位置:" + endIndex); new DownThread(path, startIndex, endIndex, i).start(); } } } private static class DownThread extends Thread{ private String path; private long startIndex; private long endIndex; private int threadId; public DownThread(String path, long startIndex, long endIndex, int threadId) { super(); this.path = path; this.startIndex = startIndex; this.endIndex = endIndex; this.threadId = threadId; } @Override public void run() { int total = 0; try { File positionFile = new File(threadId + ".txt"); URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); //接著上次的檔案繼續下載 if(positionFile.exists() && positionFile.length() > 0){ FileInputStream fis = new FileInputStream(positionFile); BufferedReader reader = new BufferedReader(new InputStreamReader(fis)); //擷取當前線程上次下載的總大小是多少 String lasttotalstr = reader.readLine(); int lastTotal = Integer.valueOf(lasttotalstr); System.out.println("上次線程下載的總大小:" + lastTotal); startIndex += lastTotal; total += lastTotal; fis.close(); } conn.setReadTimeout(5000); conn.setRequestProperty("Range", "bytes=" + startIndex + "-" + endIndex);//設定伺服器上的檔案的讀取位置 int code = conn.getResponseCode(); if(code / 100 == 2){ InputStream is = conn.getInputStream(); File file = new File("temp.rar"); RandomAccessFile raf = new RandomAccessFile(file, "rw"); raf.seek(startIndex); System.out.println("第" + threadId + "個檔案的開始位置:" + String.valueOf(startIndex)); int len = 0; byte[] buffer = new byte[1024]; while ((len = is.read(buffer)) != -1){ RandomAccessFile rf = new RandomAccessFile(positionFile, "rwd"); raf.write(buffer, 0, len);//寫檔案 total += len; rf.write(String.valueOf(total).getBytes()); rf.close(); } is.close(); raf.close(); } } catch (Exception e) { e.printStackTrace(); }finally{ synchronized (DownThread.class) { System.out.println("線程" + threadId + "下載完畢了"); runningThreadCount--; if (runningThreadCount < 1) { System.out.println("所有的線程都工作完畢了。刪除臨時記錄的檔案"); for (int i = 0; i < threadCount; i++) { File f = new File(i + ".txt"); System.out.println(f.delete()); } } } } } }}
運行結果:
Java之多線程斷點下載的實現