- 首先看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; } }