RandomAccessFile分割、还原文件(二)
RandomAccessFileadmin 发布于:2015-12-11 09:37:14
阅读:loading
前文中有一个简单版本的示例,基于彼示例,我做了进一步的优化调整,添加了分割文件的配置属性文件,属性文件一般可以是properties类型的,这里我就采用序列化文件,直接将一个存储好数据的对象写入文件,还原文件时再读入此文件作为还原的标准配置。
记录的属性有以下几个:
fileName源文件名称:用于还原文件时恢复分割前的文件原名称;
fileLength源文件大小:可用于还原文件前检查现有的还原目录的可用磁盘是否充足;
fileSuffix源文件后缀:作用不大可作为预留属性值;
partList分割的碎片:按照先后顺序存储所有被分割的碎片文件,还原文件时按照此组数据的先后顺序进行先后写入;
原理同前一篇文章相同,只是功能越完善需要添加的代码和逻辑也就越多了,参考代码如下:
1.文件分割
package com;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.RandomAccessFile;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 按指定文件大小分割文件,并记录文件分割信息
* @author chendd
*
*/
public class SplitFile {
private long blockLength;//分割单个文件大小
private long currentTotal;//当前分割次数的位置
private Map<String , Object> propMap;//记录本次分割文件的信息
public SplitFile(int blockLength) {
this.blockLength = blockLength;
propMap = new HashMap<String , Object>();
}
public void split(File srcFile , String splitFolder){
RandomAccessFile raf = null;
try {
long fileLens = srcFile.length();//文件大小
if(fileLens <= this.blockLength){
System.out.println("分割的文件大小 " + this.blockLength + " 大于文件的大小!");
return;
}
raf = new RandomAccessFile(srcFile, "r");
//根据分割块大小,计算一共有多少块文件
int blockCount = (int) (fileLens % this.blockLength);
if(blockCount == 0){
blockCount = (int) (fileLens / this.blockLength);
}else{
blockCount = (int) (fileLens / this.blockLength) + 1;
}
//按快进行读取文件
String srcFileName = srcFile.getName();
//记录源文件名称
propMap.put("fileName" , srcFileName);//原始文件名称
propMap.put("fileLength" , this.getFileLength(srcFile));
propMap.put("fileSuffix" , this.getFileSuffix(srcFileName));
List<String> partList = new ArrayList<String>();
for(int i=0 ; i < blockCount ; i++){
File destFile = new File(splitFolder + File.separator + srcFileName + "." + (i+1) + ".part");
String partFileName = destFile.getName();
partList.add(partFileName);
splitFileDetail(destFile , raf);//分割文件
}
propMap.put("partList" , partList);//碎片文件列表
//记录还原文件相关的数据,用序列化文件
writePropFile(propMap , srcFile , splitFolder);
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally{
if(raf != null){
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 将记录的文件相关的数据写入文件
* @param propMap 写入文件属性对象
* @param srcFileName 源文件名称
*/
private void writePropFile(Map<String, Object> propMap , File srcFile , String splitFolder) {
String newName = srcFile.getName();
int nameIndex = newName.lastIndexOf(".");
if(nameIndex != -1){
newName = newName.substring(0, nameIndex);
}
File propFile = new File(splitFolder, newName + ".obj");
ObjectOutputStream ois = null;
try {
ois = new ObjectOutputStream(new FileOutputStream(propFile));
ois.writeObject(propMap);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally{
if(ois != null){
try {
ois.flush();
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
private void splitFileDetail(File destFile , RandomAccessFile raf) {
byte b[] = new byte[1024];
int lens = 0;
//如果文件目录不存在,则创建
if(destFile.getParentFile().exists() == false){
destFile.getParentFile().mkdirs();
}
BufferedOutputStream bos = null;
try {
raf.seek(currentTotal);//设置开始读取的位置
long currentMax = raf.getFilePointer() + this.blockLength;
bos = new BufferedOutputStream(new FileOutputStream(destFile));
while ((lens = raf.read(b)) != -1) {
//判断文件读取的大小,避免已经读取超过每块的大小了
if (currentTotal + lens > currentMax) {
break;
}
bos.write(b, 0, lens);
currentTotal += lens;
}
} catch (Exception e) {
e.printStackTrace();
}finally{
if(bos != null){
try {
bos.flush();
bos.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
}
/**
* 根据文件名称获取文件后缀
* @param fileName 文件名称
* @return 返回.zip或.exe等
*/
private String getFileSuffix(String fileName){
int index = fileName.lastIndexOf(".");
if(index == -1){
return "";
}
return fileName.substring(index);
}
/**
* 获取文件大小
* @param srcFile 源文件
* @return 文件大小long类型
*/
private long getFileLength(File srcFile){
return srcFile.length();
}
public static void main(String[] args) {
File srcFile = new File("C:\\Users\\chendd\\Desktop\\中国政区2500.jpg");
String splitFolder = "d:\\splitFolder";//分割存储路径
SplitFile splitFile = new SplitFile(1024 * 1024);//按1M的大小去分割文件
splitFile.split(srcFile , splitFolder);
}
}
2.代码实现
package com;
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.ObjectInputStream;
import java.util.List;
import java.util.Map;
/**
* 合并文件
* @author chendd
*
*/
public class MergeFile {
public static void main(String[] args) throws IOException, ClassNotFoundException {
String propFile = "d:\\splitFolder\\中国政区2500.obj";
mergeFile(propFile);
}
/**
* 合并文件
* @param propFile 分割文件的属性文件路径
*/
private static void mergeFile(String propFile) throws IOException,
FileNotFoundException, ClassNotFoundException {
File file = new File(propFile);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
Object obj = ois.readObject();
if(obj instanceof Map == false){
System.out.println("文件以及损坏");
}
@SuppressWarnings("unchecked")
Map<String , Object> propMap = (Map<String, Object>) obj;
String fileName = (String) propMap.get("fileName");//源文件名称
/**文件大小,科学一点的判定可以判定当前磁盘可用存储空间是否可以容纳本次还原文件**/
long fileLength = (long) propMap.get("fileLength");
System.out.println("还原文件大小: " + fileLength);
//碎片文件
@SuppressWarnings("unchecked")
List<String> partList = (List<String>) propMap.get("partList");
File srcFile = new File(file.getParent() + File.separator + fileName);
if(srcFile.exists()){
srcFile.delete();
}
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(srcFile, true));
byte b[] = new byte[1024];
for (String partName : partList) {
String filePath = file.getParent() + File.separator + partName;
//循环读取文件
InputStream is = new FileInputStream(filePath);
int lens = 0;
while((lens = is.read(b)) != -1){
bos.write(b , 0 , lens);
}
is.close();
}
bos.flush();
bos.close();
}
}
以上程序代码如果想要自主运行,请注意修改一下要分割的文件路径或名称,运行的参考效果图为:
试了一下分割一个字符文件,会出现那种文件中某些汉字被割断了的情况成了??这些,如果分割文件出现乱码则可能是由于eclipse默认的GBK编码,文件自身是UTF-8编码所致,故分割文件时也要注意每次读取文件时所采用的字符编码;分割字节文件暂时没有发现什么问题。
点赞