IO学习(十七)文件的分割与合并

来源:互联网 发布:淘宝客服绩效是什么 编辑:程序博客网 时间:2024/05/10 21:36

目的:将文件分割成数个部分,然后再将它们合并起来


首先文件的分割,有下面几个要点

1.先要确定的两个因素就是,分成多少块,每块多大,那么最后一块的大小不一定刚好能是你规定的每小块的大小,那么最后一块的大小就比较特殊,它等于文件总大小(块数-1)乘以每块大小

2.在操作源文件到目的文件,即被分割文件到分割文件时,实际上就是文件的拷贝过程

3.最关键的问题是如何控制文件输入流,它必须按照指定的位置读取每一个分块

比如,我有142k大小的文件,要将他们分割成3块,规定每块大小为50,那么我将第一块内容读取的时候,是从0-50K

当读第二快内容时就变成了50-100K,那么如何控制读取范围?

这里就需要用到RandomAccessFile类,它提供了一个seek方法,可以指定读取的开始位置



文件的合并就是将那些分块重新组合在一起,比文件分割考虑的因素少,这里提供了两种不同的方式进行文件合并,其实也就是关于输入流做出的改变,详细见下文,合并从小块文件到大文件,其实也就是文件追加输出



本着OOP的设计思想,将对文件的信息和操作方法封装成一个类 SplitFile

这个类包含了关于文件的一些基本信息,文件的路径,大小,名称等,以及为了分割需要的一些信息,分割块数,每块的大小,分割文件所在的目录文件,各自的名称等,下面开始一步一步实现代码



1.类属性+构造方法

在构造方法运行结束后,初始化也结束,关于每块大小可以在创建对象的时候设置,也可以使用默认的1024

public class SplitFile {//文件路径private String filePath;//文件名称private String fileName;//文件大小private long length;//块数private int size;//每块大小private long blockSize;//每块名称private List<String> blockPath;//分割后的目录private String destPath;public SplitFile() {this.blockPath=new ArrayList<String>();}public SplitFile(String filePath,String destPath){this(filePath,1024,destPath);}public SplitFile(String filePath, long blockSize,String destPath) {this();this.filePath = filePath;this.blockSize = blockSize;this.destPath=destPath;init();}}



2.初始化分割信息

确定分割块数,先得到文件长度length

size=(int)(Math.ceil(length*1.0/this.blockSize));


确定部分文件的名称

由于将所有小文件的名称存放在List中,所以这里用一个for循环,往List中添加元素

其中destPath是存放部分文件的目录

for(int i=0;i<this.size;i++){
this.blockPath.add(destPath+"/"+this.fileName+".part"+i);
}




3.文件的分割

这里需要两个很关键的值,beginPos和actualBlockSize,每个部分文件开始的位置,以及它的大小

由于每一个分块这些信息都不完全相同,所以需要分别处理

使用for循环遍历所有的块,需要注意的是,我们的size是向下取整,也就是说当计算的值为3.6时,size的值为3,但是这里的i从0开始,所以一切正常

long beginPos=0;
long actualBlockSize=blockSize;

for(int i=0;i<size;i++){
//计算最后一块大小
if(i==size-1){
actualBlockSize=this.length-beginPos;
}
spiltDetail(i,beginPos,actualBlockSize);
beginPos+=actualBlockSize;
}


spiltDetail方法其实就是真正文件分割的过程,其实也就是文件拷贝,4个步骤

选择目标文件和源文件;选择输入输出流;进行拷贝;关闭流

在拷贝过程中,需要注意不能一直都以固定的缓冲长度来写出数据,需要进行判断

while (-1 != (len = raf.read(flush))) {
if(actualBlockSize-len>=0){
bos.write(flush,0,len);
actualBlockSize-=len;
}else{
bos.write(flush,0,(int)actualBlockSize);
break;
}
}






文件的合并就比较简单了,第二种方法中用到了SequenceInputStream类,将很多个输入流集中在一起,只所有有很多个输入流是由于每一个部分文件对应一个输入流

要使用这个类就要先有一个集合,这里用Vector<InputStream>创建一个带泛型的Vector对象

使用for循环将文件输入流添加到容器中,然后构建SequenceInputStream对象

SequenceInputStream sis = new SequenceInputStream(vi.elements());



为了增强代码的健壮性,还添加了一些判断,下面是全部代码


public class SplitFile {//文件路径private String filePath;//文件名称private String fileName;//文件大小private long length;//块数private int size;//每块大小private long blockSize;//每块名称private List<String> blockPath;//分割后的目录private String destPath;public SplitFile() {this.blockPath=new ArrayList<String>();}public SplitFile(String filePath,String destPath){this(filePath,1024,destPath);}public SplitFile(String filePath, long blockSize,String destPath) {this();this.filePath = filePath;this.blockSize = blockSize;this.destPath=destPath;init();}/** * 初始化操作,确定块数 */public void init(){File src=null;//如果文件民为空或者文件不存在,直接returnif(null==this.filePath || !( (src=new File(this.filePath)).exists())){return ;}//如果是一个文件夹,直接returnif(src.isDirectory()){return;}//得到文件实际大小与名称this.length=src.length();this.fileName=src.getName();//如果每块大小大于文件大小,则修改每块大小if(this.blockSize>length){this.blockSize=length;}//计算块数,向下取整size=(int)(Math.ceil(length*1.0/this.blockSize));}/** * 初始化部分文件名称 */private void initPathName(String destPath){for(int i=0;i<this.size;i++){this.blockPath.add(destPath+"/"+this.fileName+".part"+i);System.out.println("initpathname");}}/** * 文件的分割 * @param destPath 分割文件存放目录 */public void split(String destPath){//设置路径initPathName(destPath);long beginPos=0;long actualBlockSize=blockSize;//计算所有块的大小,位置,索引for(int i=0;i<size;i++){//计算最后一块大小if(i==size-1){actualBlockSize=this.length-beginPos;}spiltDetail(i,beginPos,actualBlockSize);beginPos+=actualBlockSize;}}/** * 文件分割详细情况-文件拷贝 * @param idx 第几块 * @param beginPos 起始点 * @param actualBlockSize 实际大小 */private void spiltDetail(int idx,long beginPos,long actualBlockSize){File src=new File(filePath);File dest=new File(this.blockPath.get(idx));RandomAccessFile raf=null;BufferedOutputStream bos=null;try {raf = new RandomAccessFile(src, "r");bos = new BufferedOutputStream(new FileOutputStream(dest));raf.seek(beginPos);int len = 0;byte[] flush = new byte[1024];while (-1 != (len = raf.read(flush))) {//查看是否足够写出if(actualBlockSize-len>=0){bos.write(flush,0,len);actualBlockSize-=len;}else{bos.write(flush,0,(int)actualBlockSize);break;}}} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}finally{try {bos.close();raf.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}/** * 文件合并 * @param args */public void mergeFile1(String destPath){File dest=new File(destPath);BufferedInputStream bis = null;BufferedOutputStream bos = null;try {for (int i = 0; i < this.blockPath.size(); i++) {bis = new BufferedInputStream(new FileInputStream(new File(blockPath.get(i))));// 设置为追加bos = new BufferedOutputStream(new FileOutputStream(dest, true));int len = 0;byte[] flush = new byte[1024];while (-1 != (len = bis.read(flush))) {bos.write(flush, 0, len);}bos.flush();bis.close();}} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}finally{try {bos.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}/** * 文件合并2 * 使用SequenceInputStream * @param args */public void mergeFile2(String destPath){File dest=new File(destPath);SequenceInputStream sis=null;BufferedOutputStream bos = null;//创建一个容器Vector<InputStream> vi=new Vector<InputStream>();try{for(int i=0;i<this.blockPath.size();i++){vi.add(new BufferedInputStream(new FileInputStream(new File(blockPath.get(i)))));}// 设置为追加bos = new BufferedOutputStream(new FileOutputStream(dest, true));sis = new SequenceInputStream(vi.elements());int len = 0;byte[] flush = new byte[1024];while (-1 != (len = sis.read(flush))) {bos.write(flush, 0, len);}bos.flush();sis.close();} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();} finally{try {bos.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}



0 0