我们在下载东西可以发现当我们网络断开之后,下载进度依然保存,当网络恢复时,接着断点位置继续下载,其实基于RandomAccessFile流就可以实现这样的操作,下面用个文件拷贝演示一下原理。
java.io.RandomAccessFile
该类的实例支持读取和写入随机访问文件,它既可以读取文件也可以写入文件,只需要在定义构造器时形参第二位声明“r” (可读) “rw” (可读可取),RandomAccessFile对象包含了一个记录指针,当程序新创建一个RandomAccessFile对象时,指针位于0处,当开始读取文件时指针后移,移动的偏移量就是读取的字节数,下面有两个方法可以获取、设置指针位置:
getFilePointer():获取当前指针位置,返回long类型数值
seek(long pos):设置当前指针位置为pos
好了,了解了原理之后我们就可以开始了:
创建RandomAccessFileDemo类
public class RandomAccessFileDemo {
// 源文件
File source;
// 目标文件
File target;
// 记录断点指针的文件
File breakPoint;
/**
* 定义有参构造器用来初始化
*
* @param source
* @param target
* @param breakPoint
*/
public RandomAccessFileDemo(File source, File target, File breakPoint) {
this.source = source;
this.target = target;
this.breakPoint = breakPoint;
}
/**
* 定义文件拷贝方法
*
* @throws FileNotFoundException
*/
public void copyFile() throws FileNotFoundException {
// 定义读取流,设置为可读状态
RandomAccessFile raf1 = new RandomAccessFile(source, "r");
// 定义写入流,设置为可写可读状态
RandomAccessFile raf2 = new RandomAccessFile(target+File.separator+source.getName(), "rw");
// 定义写入流,把指针位置保存在记录断点文件
RandomAccessFile raf3 = new RandomAccessFile(breakPoint, "rw");
// 定义变量用来记录read方法实际读取长度
int len = 0;
// 定义缓存大小为16字节的字节数组
byte b[] = new byte[16];
// 这里我们定义一个“意外”让读写中断
try {
// 定义计数器
int count = 0;
while ((len = raf1.read(b)) != -1) {
raf2.write(b, 0, len);
count++;
// 计数器到三拷贝中断
if (count == 3) {
// 得到断点时读入指针位置,也是恢复时开始读写的位置
long point = raf1.getFilePointer();
// 将断点位置记录到保存指针位置文件中
raf3.writeLong(point);
System.out.println("拷贝中断");
break;
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭流
try {
raf1.close();
raf2.close();
raf3.close();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
/**
* 创建recover方法,恢复断点,继续拷贝
* 这里我们用来演示,实际开发中不建议这样写(代码重复率太高)
* @throws FileNotFoundException
*/
public void recover() throws FileNotFoundException{
// 定义读取流,设置为可读状态
RandomAccessFile raf1 = new RandomAccessFile(source, "r");
// 定义写入流,设置为可写可读状态
RandomAccessFile raf2 = new RandomAccessFile(target+File.separator+source.getName(), "rw");
// 定义写入流,把指针位置保存在记录断点文件
RandomAccessFile raf3 = new RandomAccessFile(breakPoint, "rw");
// 定义变量用来记录read方法实际读取长度
int len = 0;
// 定义缓存大小为16字节的字节数组
byte b[] = new byte[16];
/*
* 写入文件之前我们要把指针位置设置到断点位置
* 先读出保存在文件中指针的位置
*/
try {
long point=raf3.readLong();
//将指针位置恢复
raf1.seek(point);
raf2.seek(point);
} catch (IOException e) {
e.printStackTrace();
}
//继续写入文件,从当前指针读写
try {
while ((len = raf1.read(b)) != -1) {
raf2.write(b, 0, len);
}
System.out.println("拷贝完成");
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
//关流
raf1.close();
raf2.close();
raf3.close();
} catch (IOException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
}
}
}
}
创建测试类
/**
* 测试
* @author gq
*
*/
public class Test {
public static void main(String[] args) throws FileNotFoundException {
//这里我们用音频文件演示
File source=new File("D:\\Test\\梅艳芳 - 亲密爱人.mp3");
File target=new File("D:\\Test\\music");
File breakPoint=new File("D:\\Test\\pointer.txt");
RandomAccessFileDemo r=new RandomAccessFileDemo(source, target, breakPoint);
//先拷贝
//r.copyFile();
//从断点位置继续拷贝
r.recover();
}
}
执行了拷贝之后我们发现文件只复制了一部分:
此时文件打不开.
下面我们执行第二个方法恢复拷贝,文件与源文件大小一致:
文件可以正常播放。
这时我们发现文件已经完全拷贝过去了,其实网络进行下载传输时,也是这个原理,当网路中断时记录断点位置,等网络恢复时继续从断点位置接着下载。