- 首先看RandomAccessFile類的api說明:
-
該類的實例支持讀取和寫入隨機訪問文件。 隨機訪問文件的行為類似於存儲在文件系統中的大量字節。 有一種游標,或索引到隱含的數組,稱為 文件指針 ; 輸入操作讀取從文件指針開始的字節,並使文件指針超過讀取的字節。 如果在讀/寫模式下創建隨機訪問文件,則輸出操作也可用; 輸出操作從文件指針開始寫入字節,並將文件指針提前到寫入的字節。 寫入隱式數組的當前端的輸出操作會導致擴展數組。 文件指針可以通過讀取
getFilePointer
方法和由設置seek
方法。 - 大意就是把文件當做數組,通過下標去讀寫,該下標即是我們所說的文件指針。可以通過上述的兩種方法去獲取、設置文件指針所指的位置。
-
- 斷點續傳的思路:
- 既然可以設置指針的位置,那么我們可以記錄下當前拷貝到的位置,當程序意外中斷或者暫停重新運行后,再去讀取之前記錄位置的值,設置文件指針到該位置以達到繼續傳輸而不是從頭開始的效果。
- 問題:怎么保存指針位置值?
- 由於程序結束后所有值都會從內存中清除,只能通過外部值來存儲,第一種是:單線程拷貝下將指針設置為已拷貝文件的長度(length),多線程下是不可行的,因為多個線程在同時寫入一個目標文件,無法獲取到每一個分段已拷貝的大小。第二種是:文件指針存儲到文件中。
- 本例需求:
- 采用多線程技術,實現多線程斷點續傳,要求線程的數量可由客戶端程序來設置
//Test01

package com.wxg.download_threads; import java.io.File; import java.util.Scanner; public class Test01 { public static void main(String[] args) throws Exception { File sourceFile=new File("性感荷官在線發牌.avi"); File targetFile=new File("copy.avi"); Scanner scan = new Scanner(System.in); System.out.println("請輸入需要啟動的線程數量(最多8個)"); int copyNum=scan.nextInt(); scan.close(); if(copyNum>8||copyNum<=0){ System.out.println("輸入錯誤"); return; } long copySize=sourceFile.length()/copyNum;//計算前copyNum-1個線程拷貝文件的分段大小 int i; for(i=0;i<copyNum-1;i++){ new DownloadUtilThreads(sourceFile, targetFile, copySize, copySize*i).start(); } new DownloadUtilThreads(sourceFile, targetFile, copySize+(sourceFile.length()%copyNum), copySize*(i+1)).start(); } }
//拷貝線程:DownloadUtilThreads

package com.wxg.download_threads; import java.io.File; import java.io.FileNotFoundException; import java.io.RandomAccessFile; public class DownloadUtilThreads extends Thread { private long copySize; private long point; private RandomAccessFile r; private RandomAccessFile w; private File logFile; public DownloadUtilThreads(File sourceFile,File targetFile,long copySize,long point) throws FileNotFoundException { this.copySize=copySize; this.point=point; r=new RandomAccessFile(sourceFile, "r"); w=new RandomAccessFile(targetFile, "rw"); } @Override public void run() { try { //創建日志操作對象 logFile=new File(Thread.currentThread().getName()+"_download.log"); LogOpreator logOpreator = new LogOpreator(logFile); //首次啟動下載 if(logFile.length()==0){ logOpreator.write(point,false); } //讀取日志文件取出point,isFinish long startIndex = logOpreator.readPoint(); boolean isFinish = logOpreator.readIsFinish(); System.out.println(startIndex+"---"+isFinish); //判斷是否已經下載完成 if(isFinish){ return; } //設置指針偏移 r.seek(startIndex); w.seek(startIndex); //拷貝 byte[] b=new byte[8192]; int len; while((len=r.read(b))!=-1){ w.write(b, 0, len); startIndex+=len; logOpreator.write(startIndex,false); //判斷拷貝是否完成 if(startIndex>=copySize){ logOpreator.write(startIndex,true); System.out.println(startIndex+"---"+isFinish); break; } } } catch (Exception e) { e.printStackTrace(); } } }
//實體存儲對象:DownloadLog(其中point用於存儲文件指針,isFinish用於存儲文件是否拷貝完)

package com.wxg.download_threads; import java.io.Serializable; public class DownloadLog implements Serializable { /** * */ private static final long serialVersionUID = 1L; private long point; private boolean isFinish=false; public DownloadLog() { } public DownloadLog(long point,boolean isFinish) { this.point = point; this.isFinish=isFinish; } public long getPoint() { return point; } public void setPoint(long point) { this.point = point; } @Override public String toString() { return "DownloadLog [point=" + point + "]"; } public boolean isFinish() { return isFinish; } public void setFinish(boolean isFinsh) { this.isFinish = isFinsh; } }
//日志讀寫操作:LogOpreator

package com.wxg.download_threads; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; public class LogOpreator { private DownloadLog d; private ObjectInputStream obi; private ObjectOutputStream obo; private File file; public LogOpreator(File file) { this.file = file; } //寫入對象到文件 public void write(long point,boolean isFinish){ d=new DownloadLog(point,isFinish); try { obo=new ObjectOutputStream(new FileOutputStream(file)); obo.writeObject(d); obo.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } //取出point public long readPoint(){ try { obi=new ObjectInputStream(new FileInputStream(file)); Object logOb = obi.readObject(); DownloadLog log=(DownloadLog)logOb; obi.close(); return log.getPoint(); } catch (Exception e) { e.printStackTrace(); } return 0; } public boolean readIsFinish(){ try { obi=new ObjectInputStream(new FileInputStream(file)); Object logOb = obi.readObject(); DownloadLog log=(DownloadLog)logOb; obi.close(); return log.isFinish(); } catch (Exception e) { e.printStackTrace(); } return false; } }