学习Java也有近一个月了,基础语法、常用类也有所了解。上周把文件相关操作进行了一个总结,制作了一个简单的文件常用操作的工具类。
此处详解文件的打包与解包
工具类包含了容下功能的实现:
1、 拷贝文件
2、 移动文件
3、 遍历文件夹
4、 获取文件夹内所有文件
5、 文件打包与解包
5-1 文件的打包
思路:要将文件进行打包,首先需要拿到文件当中的所有文件对象,然后将所有的文件安装一定的组织关系存入一个文件当中。
既然是打包,那么就应该有一个打包后的文件类型,在这里我将包文件类型定义为pakge类型的文件
public static String PACKAGE_TYPE = ".pakge";
在上面已经实现了获取文件中的所有文件,现在需要将所有的文件存入一个文件当中。这个就简单了,只需要遍历获取了所有的文件对象的List集合对象,将每一个文件依次写入一个文件当中即可。
/**
* 打包一个文件(夹)
* @param srcDir 要打包的目标文件(夹)绝对路径字符串
* @param dir 打包文件的存贮绝对路径字符串
* @return boolean TRUE表示成功打包
*/
public static boolean packageFile(String srcDir, String dir) {
//创建文件对象
File srcFile = new File(srcDir);
File destFile = new File(dir + PACKAGE_TYPE);
//通过文件对象获取源文件的绝对路径,避免参数字符差异
String srcFilePath = srcFile.getAbsolutePath();
//如果源文件存在,则进行打包操作
if (srcFile.exists()) {
//通过已有方法获取所有文件
ArrayList<File> fileList = new ArrayList<File>();
FileControls.getAllFiles(srcFilePath, fileList);
try {
//以追加方式创建文件输出流,绑定打包文件
FileOutputStream fs = new FileOutputStream(destFile, true);
for (Filef : fileList) {
if (!f.getFilePath().isDirectory()) {
FileInputStream fi = new FileInputStream(f.getFilePath());
//创建缓冲字节数组
byte[] buffer = new byte[4096];
//获取当前文件总长(字节数)
long size = f.length();
while (true) {
//如果未写入的字节数比缓冲数组长,则读取并写入4096字节 if (size > 4096) {
fi.read(buffer);
fs.write(buffer);
//未写入字节数减少4096
size -= 4096;
} else {
//读取并写入剩余字节,关闭输入流
fi.read(buffer, 0, (int) size);
fs.write(buffer, 0, (int) size);
fi.close();
break;
}
}
}
}
//刷新输出流,关闭输出流
fs.flush();
fs.close();
return true;
} catch (IOException e) {
e.printStackTrace();
}
} else {
System.out.println("源文件\"" + srcFile.getAbsolutePath() + "\"不存在");
}
return false;
}
这里可以看到,打包功能已经完成。接下来看解包的实现。
5-2 文件的解包
思路:在这里突然发现,上面实现的打包功能无法解包。为啥呢?因为包中的文件名、文件大小信息均没有进行保留。因此需要对上面的打包功能进行优化。在打包的时候要将文件名、文件大小写入到打包文件中。这样在解包的时候才可以将包文件中的文件正常的完整的解包出来。
因此,增加了一个功能类Filesize
public static class Filesize implements Serializable {
private static final long serialVersionUID = -1035346214239079079L;
private File filePath;
private long fileSize;
public File getFilePath() {
return filePath;
}
public long getFileSize() {
return fileSize;
}
public void setFilePath(String filePath) {
this.filePath = new File(filePath);
this.fileSize = this.filePath.length();
}
}
该功能类Filesize有两个属性:1、文件对象。2、文件对象的大小。另外可以看到该功能类实现了Serializable接口,这是为了能够该类对象能够序列化到文件中。以便于将文件的名称、大小的信息保存下来。
因为要将文件信息保存到该功能类中,因此重载获取所有文件对象的方法。重载时发现,目录文件不需要进行获取
public static void getAllFiles(String srcPath, ArrayList<FileControls.Filesize> files) {
File file = new File(srcPath);
if (file.exists()) {
File[] fileList = file.listFiles();
if (fileList != null && fileList.length > 0) {
for (File src : fileList) {
getAllFiles(src.getAbsolutePath(), files);
}
}
if (!file.isDirectory()) {
//构建功能类对象
FileControls.Filesize fileSize = new FileControls.Filesize();
fileSize.setFilePath(file.getAbsolutePath());
files.add(fileSize);
}
}
}
打包方法优化后的代码如下
/**
* 对打包文件进行解包,默认解包路径为打包文件同级路径,解包的后的文件对象的父目录为
* 包文件父目录/包文件名
*
* @param packageFilePath 包文件绝对路径字符串
* @return 成功解包则返回TRUE
*/
public static boolean upPackges(String packageFilePath) {
File packageFile = new File(packageFilePath);
FileInputStream fin = null;
ObjectInputStream oin = null;
if (packageFile.exists()) {
try {
fin = new FileInputStream(packageFile);
oin = new ObjectInputStream(fin);
ArrayList<FileControls.Filesize> list = new ArrayList<FileControls.Filesize>();
//反序列化出文件详细信息集合对象
//如果强转失败,则会抛出ClassCastException异常,说明这不是一个大包文件
list = (ArrayList<FileControls.Filesize>) oin.readObject();
if (list.size() != 0) {
for (int i = 0; i < list.size(); i++) {
File file = list.get(i).getFilePath();
//如果该文件是目录,则直接创建目录
if (!file.isDirectory()) {
//构建包文件的绝对路径字符串
String dir = packageFile.getParent() + "\\" + file.getName();
//创建解包文件
File destfile = new File(dir);
//创建父级目录
destfile.getParentFile().mkdirs();
FileOutputStream fout = new FileOutputStream(destfile);
//获取文件大小
long filesize = list.get(i).getFileSize();
//通过缓冲法将文件类容读取并写入解包的对应文件中
byte[] buffer = new byte[4096];
while (true && filesize > 0) {
if (filesize > 4096) {
fin.read(buffer);
fout.write(buffer);
filesize -= 4096;
} else {
fin.read(buffer, 0, (int) filesize);
fout.write(buffer, 0, (int) filesize);
break;
}
}
fout.flush();
fout.close();
} else {
file.mkdirs();
}
}
//关闭输入流
oin.close();
return true;
}
} catch (ClassCastException ce) {
System.out.println("这不是一个包文件");
try {
oin.close();
fin.close();
} catch (IOException e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return false;
}
到这里,所以的功能已经完整的实现。代码已经上传到GitHub中,这是GitHub的链接