该部分完成了,文件随机读写。发送端根据片段信息,提取文件内容;接收端根据片段信息,将接收内容写入文件中。
1、文件读写器
package man.kuke.core;
import java.io.IOException;
import java.io.RandomAccessFile;
/**
* 完成对文件随机读写
*/
public class FileAccessor {
public FileAccessor() {
}
//读取文件某一位置,指定长度内容
public static byte[] readSection(String fileName, long offset,int len) throws IOException {
RandomAccessFile raf = new RandomAccessFile(fileName,"r");
raf.seek(offset);
byte[] buffer = new byte[len];
raf.read(buffer);
raf.close();
return buffer;
}
public static byte[] readSection(RandomAccessFile raf, long offset,int len) throws IOException {
raf.seek(offset);
byte[] buffer = new byte[len];
raf.read(buffer);
return buffer;
}
@SuppressWarnings("SynchronizationOnLocalVariableOrMethodParameter")
//文件写入
public static void writeSection(RandomAccessFile raf, long offset, byte[] context) throws IOException {
synchronized (raf) {
raf.seek(offset);
raf.write(context);
}
}
public static void writeSection(String fileName,long offset,byte[] context) throws IOException {
RandomAccessFile raf = new RandomAccessFile(fileName,"rw");
raf.seek(offset);
raf.write(context);
raf.close();
}
}
这里写两套文件读写。一个是读写完毕立即关闭,另一个是读写完毕不关闭,多线程通过加锁控制。本人倾向读写不立即关闭。第一种,每次读写都需要向内核申请文件资源,发生用户态切换内核态再切换为用户态,开销比加大。
2. 文件读写池
为了提高文件读写效率,文件读写后并不立即关闭。那么需要将文件id与文件对象存储到Map。这样就能保证文件仅打开或关闭一次。
package man.kuke.core;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author: kuke
* @date: 2020/11/29 - 17:07
* @description:
*/
/**
* 文件对象池子
*/
class RandomAccessFilePool {
private static final int BUFFER_SIZE = 1<<15;
private final Map<Long, RandomAccessFile> rafPool;
// 资源信息列表,提供文件路径
private ResourceInfo resourceInfo;
public RandomAccessFilePool(ResourceInfo resourceInfo) {
rafPool = new ConcurrentHashMap<>();
this.resourceInfo = resourceInfo;
}
/**
* 创建单例文件,
* 并发模式
* rafPool加锁处理
* 采用 DCL 模式
* @param fileId 文件id
* @param mod 打开方式
* @return 返回文件
* @throws FileNotFoundException 路径异常
*/
public RandomAccessFile getRandomAccessFile(Long fileId,String mod) throws FileNotFoundException {
RandomAccessFile raf = rafPool.get(fileId);
if (raf == null) {
synchronized (this.rafPool) {
raf = rafPool.get(fileId);
if (raf == null) {
raf = new RandomAccessFile(resourceInfo.getAbsoluteRoot() +resourceInfo.getFilePathById(fileId), mod);
rafPool.put(fileId, raf);
}
}
}
return raf;
}
public void closeRaf(long fileId) {
try {
RandomAccessFile raf = rafPool.remove(fileId);
if (raf == null) {
return;
}
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static byte[] readSection(RandomAccessFile file, SectionHander sectionHander) throws IOException {
byte[] buffer = new byte[BUFFER_SIZE];
file.read(buffer, (int)sectionHander.getOffset(), (int)sectionHander.getLen());
return buffer;
}
}
这里getRandomAccessFile存在线程安全问题,多线程生成文件对象,必须加锁控制,采取双检查加锁控制。RandomAccessFilePool 提供了文件关闭方法,当文件读取完毕,可以给予关闭。