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

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


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

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 blockPath;
	//分割后的目录
	private String destPath;
	
	public SplitFile() {
		this.blockPath=new ArrayList();
	}

	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;ithis.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//计算最后一块大小
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创建一个带泛型的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 blockPath;
	//分割后的目录
	private String destPath;
	
	public SplitFile() {
		this.blockPath=new ArrayList();
	}

	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;
	
		//如果文件民为空或者文件不存在,直接return
		if(null==this.filePath || !( (src=new File(this.filePath)).exists())){
			return ;
		}
		
		//如果是一个文件夹,直接return
		if(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=0){
					bos.write(flush,0,len);
					actualBlockSize-=len;
				}else{
					bos.write(flush,0,(int)actualBlockSize);
					break;
				}	
			}
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}finally{
			try {
				bos.close();
				raf.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.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 block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			try {
				bos.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	
	/**
	 * 文件合并2
	 * 使用SequenceInputStream
	 * @param args
	 */
	public void mergeFile2(String destPath){
		File dest=new File(destPath);
		SequenceInputStream sis=null;
		BufferedOutputStream bos = null;
		
		//创建一个容器
		Vector vi=new Vector();

		try{
			for(int i=0;i



你可能感兴趣的:(IO学习(十七)文件的分割与合并)