最近生产环境出现一莫名其妙的BUG,运维的同事帮忙导出了一份后台日志文件。拿到日志文件一看居然570M,这老大文件用记事本等文本工具打开那叫一个卡顿啊。于是就冒出一个想法把该巨大的日志文件分割成多个小文件来查看。这必然又让我拾起好久没有使用过的JavaIO操作了。下面是我具体文件分割与合并的实现类:
package cn.org.teemo.io.file;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.io.SequenceInputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import cn.org.teemo.io.utils.CloseUtil;
/**
* 实现对大文件的分割,并能够将分割后的文件重新合并为原文件
* @author Timeng.Hero
*
*/
public class SplitFile {
/** 文件的路径 */
private String filePath;
/** 文件名 */
private String fileName;
/** 文件大小 */
private long length;
/** 块数 */
private int blockNum;
/** 每块的大小 */
private long blockSize;
/** 分割后存放的目录 */
private String destBlockPath;
/** 每块的名称 */
private List<String> blockPath;
public SplitFile() {
blockPath = new ArrayList<String>();
}
public SplitFile(String filePath, String destBlockPath) {
this(filePath, destBlockPath, 1024);
}
public SplitFile(String filePath, String destBlockPath, long blockSize) {
this();
this.filePath = filePath;
this.blockSize = blockSize;
this.destBlockPath = destBlockPath;
init();
}
/**
* 初始化操作: 计算 块数、确定文件名
*/
public void init() {
File src = null;
// 健壮性
if (null == filePath || !(((src = new File(filePath)).exists()))) {
return;
}
if (src.isDirectory()) {
return;
}
// 文件名
this.fileName = src.getName();
// 计算块数 实际大小 与 每一块大小
this.length = src.length();
// 修正 每块大小
if (this.blockSize > length) {
this.blockSize = length;
}
// 确定块数
blockNum = (int) (Math.ceil(length * 1.0 / this.blockSize));
// 确定文件的路径
initPathName();
}
/**
* 初始化每一块的文件名
*/
private void initPathName() {
for (int i = 0; i < blockNum; i++) {
this.blockPath.add(destBlockPath + "/" + this.fileName + ".part" + i);
}
}
/**
* 文件分割 0、第几块 1、起始位置 2、实际大小
*/
public void split() {
long beginPos = 0;// 起始点
long actualBlockSize = blockSize;// 实际大小
// 计算所有块的大小、位置、索引
for (int i = 0; i < blockNum; i++) {
if (i == blockNum - 1) {// 最后一块
actualBlockSize = this.length - beginPos;
}
splitDetail(i, beginPos, actualBlockSize);
beginPos += actualBlockSize;// 本次的终点,下一次的起点
}
}
/**
* 文件的分割 (输入 输出) 文件的拷贝
*
* @param idx
* 第几块
* @param beginPos
* 起始点
* @param actuslBlockSize
* 实际大小
*/
private void splitDetail(int idx, long beginPos, long actualBlockSize) {
// 1、创建源
File src = new File(this.filePath);
File dest = new File(this.blockPath.get(idx));// 目标文件
// 2、选择流
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;
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) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
CloseUtil.closeIO(bos, raf);
}
}
/**
* 文件合并<br>
* 单一输入流循环按序输入到合并后的文件
*
* @param destPath
*/
public void merge(String destPath) {
// 创建源
File dest = new File(destPath);
// 选择流
BufferedInputStream bis = null;// 输入流
BufferedOutputStream bos = null;// 输出流
try {
bos = new BufferedOutputStream(new FileOutputStream(dest, true));// 追加
for (int i = 0; i < this.blockPath.size(); i++) {
bis = new BufferedInputStream(new FileInputStream(new File(this.blockPath.get(i))));
// 缓冲区
byte[] flush = new byte[1024];
// 接收长度
int len = 0;
while (-1 != (len = bis.read(flush))) {
bos.write(flush, 0, len);
}
bos.flush();
CloseUtil.closeIO(bis);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
CloseUtil.closeIO(bos);
}
}
/**
* 文件合并<br>
* 多个输入流汇集成一个总的输入流写入到合并后的文件中
*
* @param destPath
*/
public void mergeBySequenceInputStream(String destPath) {
// 创建源
File dest = new File(destPath);
// 选择流
BufferedOutputStream bos = null;// 输出流
SequenceInputStream sis = 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(this.blockPath.get(i)))));
}
bos = new BufferedOutputStream(new FileOutputStream(dest, true));// 追加
sis = new SequenceInputStream(vi.elements());
// 缓冲区
byte[] flush = new byte[1024];
// 接收长度
int len = 0;
while (-1 != (len = sis.read(flush))) {
bos.write(flush, 0, len);
}
bos.flush();
CloseUtil.closeIO(sis);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
CloseUtil.closeIO(bos);
}
}
/** 测试 */
public static void main(String[] args) {
SplitFile sf = new SplitFile("D:/workspace/IO/test.txt", "D:/workspace/IO", 80);
// System.out.println(sf.blockNum);
// sf.split();
// sf.merge("D:/workspace/IO/merge.txt");
sf.mergeBySequenceInputStream("D:/workspace/IO/merge.txt");
}
}