文件的分割与合并
2016-11-12 本文已影响0人
VikingOldYoung
我极限了,这个程序讲不清。我每一步都有分析,仔细看程序
设计这样的程序,都是一步一步的走,感觉少什么再完善修改前面的。但是主体的步骤要定好。
这里用到了RandomAccessFile这个类。这也是一个流。此类的实例支持对随机访问文件的读取和写入。他直接继承于Object
构造器
- RandomAccessFile(File file,String mode);
- RandomAccessFile(String filePath,String mode);
这里mode,下面两个具体看API,我们这里只用了r
-
"r":以只读方式打开。调用结果对象的任何write方法都将导致抛出 IOException。
-
"rw":打开以便读取和写入。如果该文件尚不存在,则尝试创建该文件。
-
"rws":打开以便读取和写入,对于"rw",还要求对文件的内容或元数据的每个更新都同步写入到底层存储设备。
-
"rwd" :打开以便读取和写入,对于"rw",还要求对文件内容的每个更新都同步写入到底层存储设备。
方法:就用了一个,其他的自己看,复习的时候记得看看
seek();设置到此文件开头测量到的文件指针偏移量,在该位置发生下一个读取或写入操作。
<br />
程序:这玩意啥都能分,分的时候注意文件的大小,给定的分割块的大小太小的话,那就很多文件了。看着贼麻烦。
public class FileSplit {
// 文件路径
private String filePath;
// 文件名
private String fileName;
// 文件总长度
private long length;
// 保存的路径
private String destPath;
// 快数
private int size;
// 每块的大小
private long blockSize;
// 每块的路径,存到一个数组里
private List<String> blockPath;
public FileSplit() {
this.blockPath = new ArrayList<>();
}
public FileSplit(String filePath) {
this(filePath, 1024, new File(filePath).getParent());
}
//这里搞个默认的储存点,就是原文件那个目录下
public FileSplit(String filePath, long blockSize) {
this(filePath, blockSize, new File(filePath).getParent());
}
public FileSplit(String filePath, long blockSize, String destPath) {
this();
this.filePath = filePath;
this.blockSize = blockSize;
if(!new File(destPath).isDirectory())
try {
throw new Exception();
} catch (Exception e) {
e.printStackTrace();
System.out.println("保存的路径必须为目录");
}
this.destPath = destPath;
//这里初始化直接在构造时候就完成了,因为这就俩方法,肯定构造的目的就是要分割。所以也没啥事
init();
}
/**
* 初始化操作,计算块数,确定文件名
*/
private void init() {
//先把那个文件搞出来,方便判断,是个局部变量,这个方法结束也就没有了。
File src = new File(filePath);
if (null == filePath || !src.exists()) {
return;
}
if (src.isDirectory()) {
return;
}
// 获取文件名
this.fileName = src.getName();
// 获取文件的大小
this.length = src.length();
// 修正每块的大小
if (blockSize > length) {
//这里如果大小大于文件的长度的话,就得修正他了
blockSize = length;
}
//利用了Math.ceil得到整数的块数
size = (int) Math.ceil(length * 1.0 / blockSize);
//调用私有方法,初始化每个文件的名字
initBlockPathName();
}
private void initBlockPathName() {
for (int i = 0; i < size ; i++)
//这里每个文件的地址,就是目的地址加上文件名啦
blockPath.add(destPath + "/" + fileName + ".part" + i);
}
/**
* 分割文件,起始点,每块实际大小,块数
*/
public void split() {
//起始点一开始是0
long beginPos = 0;
//每块实际大小一开始就等于给的大小
long actualBlockSize = blockSize;
//挨个来分割啦
for (int i = 0; i < size ; i++) {
//如果是最后一块,那么就存在剩的不够的情况,改变实际大小为剩的大小
if (i == size - 1) {
//总大小减去该块的起始点
actualBlockSize = this.length - beginPos;
}
splitDetil(i, beginPos, actualBlockSize);
//每次分割的起始点,都是前一块的起始点,加上实际分割的块的大小
beginPos += actualBlockSize;
}
}
/**
* 分割的细节
* @param index 索引,第几块
* @param beginPos 相对于源文件开始的位置
* @param actualBlockSize 分割的实际块大小
*/
public void splitDetil(int index, long beginPos, long actualBlockSize) {
//建立文件联系
File src = new File(this.filePath);
//获得初始化的那块的文件名
File dest = new File(this.blockPath.get(index));
//选择流
RandomAccessFile raf = null;
BufferedOutputStream bos = null;
try {
raf = new RandomAccessFile(src, "r");
bos = new BufferedOutputStream(new FileOutputStream(dest));
//跳到起始点
raf.seek(beginPos);
//读取部分源文件,保存到目标文件
byte[] flush = new byte[1024];
int len = 0;
// 如果定义的blockSize大于flush的存储量,那么下面的判断就有必要了
while (-1 != (len = raf.read(flush))) {
// 如果大于的话,把len长度的写进去,再接着读,但是blockSize就得减少了
if (actualBlockSize - len > 0) {
bos.write(flush, 0, len);
actualBlockSize -= len;
} else {
// 如果到最后了,不足len,那么就写actualBlockSize,
//然后直接跳出读取的循环,因为后面文件还是有内容的,但是这一块已经够了,所以要跳出去
bos.write(flush, 0, (int) actualBlockSize);
break;
}
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
CloseUtil.closeIO(bos, raf);
}
}
/**
* 合并文件
*/
//如果未指定路径就保存在原文件的路径下
public void mergeFile(){
mergeFile(this.destPath);
}
//给顶指定的路径
public void mergeFile(String destPath) {
BufferedInputStream bis = null;
BufferedOutputStream bos=null;
File dest=new File(destPath);
//如果给的是一个路径的话,就以原来文件的名字加个new-来新建文件
if(!dest.isFile()){
dest=new File(destPath,"merge-"+fileName);
}
try {
for (int i = 0; i < size ; i++) {
//这里读取保存的每个文件的名字
File src = new File(this.blockPath.get(i));
//输入源是那些文件
bis = new BufferedInputStream(new FileInputStream(src));
bos=new BufferedOutputStream(new FileOutputStream(dest,true));
//正常输出到目标文件,这里需要追加,在上面的FileOutputStream里面标识了
byte[] flush=new byte[1024];
int len=0;
while(-1!=(len=bis.read(flush))){
bos.write(flush,0,len);
}
//刷新是个好习惯
bos.flush();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
//关闭资源
CloseUtil.closeIO(bos,bis);
}
}
public static void main(String[] args) {
FileSplit fs = new FileSplit("f:/javaIotest/poetry.txt", 30);
fs.split();
fs.mergeFile();
}
}
<br />
关于文件的合并,里面用到了很多的输入流,每个分开操作。可以使用一个SequenceInputStream来把那些输入流合并到一起。然后当作一个流来输入。方便。以下是用法。
public void mergeFile2(String destPath) {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
// SequenceInputStream的构造起要求是个枚举
SequenceInputStream sis = null;
// 这里选择继承了枚举接口的容器,Vector。把InputStream放进去
Vector<InputStream> v = new Vector<>();
File dest = new File(destPath);
// 如果给的是一个路径的话,就以原来文件的名字加个new-来新建文件
if (!dest.isFile()) {
dest = new File(destPath, "merge-" + fileName);
}
try {
for (int i = 0; i < size; i++) {
// 这里读取保存的每个文件的名字
File src = new File(this.blockPath.get(i));
// 输入源是那些文件
bis = new BufferedInputStream(new FileInputStream(src));
v.add(bis);
}
sis = new SequenceInputStream(v.elements());
bos = new BufferedOutputStream(new FileOutputStream(dest, true));
byte[] flush = new byte[1024];
int len = 0;
while (-1 != (len = sis.read(flush))) {
bos.write(flush, 0, len);
bos.flush();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
CloseUtil.closeIO(sis, bos, bis);
}
}