RandomAccessFile概述
下面是引自jdk1.6的api中对这个类的描述:
此类的实例支持对随机访问文件的读取和写入。随机访问文件的行为类似存储在文件系统中的一个大型 byte 数组。存在指向该隐含数组的光标或索引,称为文件指针;输入操作从文件指针开始读取字节,并随着对字节的读取而前移此文件指针。如果随机访问文件以读取/写入模式创建,则输出操作也可用;输出操作从文件指针开始写入字节,并随着对字节的写入而前移此文件指针。写入隐含数组的当前末尾之后的输出操作导致该数组扩展。该文件指针可以通过 getFilePointer 方法读取,并通过 seek 方法设置。
通常,如果此类中的所有读取例程在读取所需数量的字节之前已到达文件末尾,则抛出 EOFException(是一种 IOException)。如果由于某些原因无法读取任何字节,而不是在读取所需数量的字节之前已到达文件末尾,则抛出 IOException,而不是 EOFException。需要特别指出的是,如果流已被关闭,则可能抛出 IOException。
代码实现
- 要实现并发复制文件,主要通过RandomAccessFile类的seek方法实现,然后
seek方法描述:
public void seek(long pos)
throws IOException设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。偏移量的设置可能会超出文件末尾。偏移量的设置超出文件末尾不会改变文件的长度。只有在偏移量的设置超出文件末尾的情况下对文件进行写入才会更改其长度。
参数:
pos - 从文件开头以字节为单位测量的偏移量位置,在该位置设置文件指针。
抛出:
IOException - 如果 pos 小于 0 或者发生 I/O 错误。
实现代码
文件复制线程
- 首先要先实现一个文件复制的线程,可以根据源文件路径,目标文件路径,复制文件大小,从文件哪个地方开始复制,代码如下:
/**
* 文件复制线程
* @author Administrator
*
*/
class CopyFileThread implements Runnable{
// 源文件路径
private String souceFile;
// 目标文件路径
private String targetFile;
// 复制文件长度
private Long copyLength;
// seek指定文件位置
private Long seekIndex;
public CopyFileThread(String souceFile, String targetFile, long copyLength , long seekIndex) {
this.souceFile = souceFile;
this.targetFile = targetFile;
this.copyLength = copyLength;
this.seekIndex = seekIndex;
}
@Override
public void run() {
// 设置读写文件缓冲区,减少文件读写对磁盘的访问次数,提高性能
byte [] buffer = new byte[1024];
try ( RandomAccessFile souce = new RandomAccessFile(souceFile, "rw") ;
RandomAccessFile target = new RandomAccessFile(targetFile, "rw") ;) {
// 计算需要读取的文件次数
long readCount = copyLength / 1024;
for (int i = 0; i < readCount; i++) {
if (i != readCount -1 ) {
// 不是最后一次时
souce.read(buffer);
target.write(buffer);
} else {
// 最后一次时,获取剩余文件长度
int tempBufferLength = 1024 + (int)(copyLength % 1024);
byte[] tempBuffer = new byte[tempBufferLength];
souce.read(tempBuffer);
target.write(tempBuffer);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
实现并发复制
package io.file;
import java.io.RandomAccessFile ;
public class CopyFileByRand {
// 定义每个线程复制的文件块大小
private final int BLOCK = 1024 * 1024 * 64;
/**
* 多线程复制文件
* @param souceFile 源文件存放路径
* @param targetFile 复制文件存放路径
*/
public void currentCopyFile(String souceFile, String targetFile){
// jdk7新写法,try()表示最终try块执行完毕之后,()中的资源会被释放,等同于在finally块中释放资源
try (
RandomAccessFile source = new RandomAccessFile(souceFile, "rw");
RandomAccessFile target = new RandomAccessFile(targetFile, "rw");
) {
// 获取文件长度
long fileLength = source.length();
// 获取需要几个线程进行读取,最后一个线程读取剩余的
long count = fileLength / BLOCK;
for (int i = 0; i < count; i++) {
// 设置目标文件长度
target.setLength(fileLength);
// "rw" 表示文件可读可写
source.seek(i * BLOCK);
target.seek(i * BLOCK);
// 判断是否是最后一次
if (i == count -1) {
// 最后一次时,复制文件长度=文件块大小+最后一小块因为fileLength / BLOCK丢失的小数部分文件块大小
long copyFileLength = BLOCK + fileLength % BLOCK;
// 不是最后一次,文件长度就等于文件块长度
new Thread( new CopyFileThread(souceFile, targetFile, copyFileLength, i * BLOCK)).start();
} else{
// 不是最后一次,文件长度就等于文件块长度
new Thread( new CopyFileThread(souceFile, targetFile, BLOCK, i * BLOCK)).start();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String [] args) {
CopyFileByRand copy = new CopyFileByRand();
copy.currentCopyFile("G:/迅雷下载/变形金刚5.mp4", "E:/变形金刚5.mp4");
}
}