您的位置:首页 > 其它

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

2016-05-22 00:39 267 查看
目的:将文件分割成数个部分,然后再将它们合并起来

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

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;

//如果文件民为空或者文件不存在,直接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<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 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<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 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();
}
}
}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: