通过一个文件拷贝案例认识RandomAccessFile
RandomAccessFile
- 它是java.io包下的类,同时实现了DataOutput, DataInput;即可以输入流也可以是输出流。
- 可以指定位置进行读写操作。(网上很多文件分片上传的案例都是基于该特性实现的。)
并发拷贝的思路描述
1.需求:我有一个大小为(fileSize)150M的视频文件需要复制,准备分给2条线程去执行。
2.根据需求我们很容易想到第一个线程需要复制第0M到75M的字节用数学区间表示为[0,75),第二个线程需要复制[75,150)
代码实现
package com.lxh.task;
import java.io.File;
import java.io.RandomAccessFile;
import java.util.concurrent.CountDownLatch;
public class CopyTask implements Runnable {
private File source;
private File copy;
private long position;
private CountDownLatch countDownLatch;
private int bufferSize;
private long partSize;
/**
* @param source 源文件
* @param copy 复制得到的文件
* @param position 文件读写位置
* @param countDownLatch 线程计数器
* @param bufferSize 缓存区大小
* @param partSize 分片大小
*/
public CopyTask(File source, File copy, long position, CountDownLatch countDownLatch, int bufferSize,long partSize) {
this.source = source;
this.copy = copy;
this.position = position;
this.countDownLatch = countDownLatch;
this.bufferSize = bufferSize;
this.partSize = partSize;
}
@Override
public void run() {
try {
RandomAccessFile srcRaf = new RandomAccessFile(source,"r");
RandomAccessFile copyRaf = new RandomAccessFile(copy,"rw");
srcRaf.seek(position);
copyRaf.seek(position);
long begin = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName()+"从文件位置:"+position+"开始读写操作");
int len = -1;
byte[] buffer = new byte[bufferSize];
long tLen = 0;//记录当前线程读了多数字节
while ((len=srcRaf.read(buffer))>0){
copyRaf.write(buffer,0,len);
tLen+=len;
if(tLen >= partSize){
break;
}
}
System.out.println(Thread.currentThread().getName()+"文件拷贝结束,共拷贝了:"
+tLen+ "个字节"+"用时:"+(System.currentTimeMillis()-begin)+"毫秒");
srcRaf.close();
copyRaf.close();
countDownLatch.countDown();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
@Test
public void testThreadPoolCopy() throws Exception{
int threadNum = 2;
int bufferSize = 1024*8;
File srcFile = new File("/home/lxq/IdeaProjects/RandomAccesFile/1/150.mp4");
File copyFile = new File("/home/lxq/IdeaProjects/RandomAccesFile/2/150.mp4");
long fileSize = srcFile.length();
long partSize = (fileSize%threadNum==0) ? fileSize/threadNum : fileSize/threadNum+1;
ExecutorService pool = Executors.newFixedThreadPool(threadNum);
long begin = System.currentTimeMillis();
CountDownLatch countDownLatch = new CountDownLatch(threadNum);
System.out.println(Instant.now() +Thread.currentThread().getName()+"开始复制");
for(int i=0;i<threadNum;i++){
pool.submit(new CopyTask(srcFile,copyFile,i*partSize,countDownLatch,bufferSize,partSize));
}
countDownLatch.await();
long end = System.currentTimeMillis();
System.out.println(Instant.now() +Thread.currentThread().getName()+"复制完成,用时:"+(end-begin)+"毫秒");
}
代码执行后用diff命令对比,两个文件的二进制有无不一样,如果没有就证明代码运行是成功的。
diff /home/lxq/IdeaProjects/RandomAccesFile/1/150.mp4 /home/lxq/IdeaProjects/RandomAccesFile/2/150.mp4
总结:
- 通过RandomAccessFile构造方法中指定mode为r或者rw,决定是输出流还是输入流。
- 通过seek方法可以移动文件指针位置。