java学习笔记:使用zip api进行文件解压缩以及不解压直接读取指定文件内容

时间:2022-09-22 21:37:54

在一个android项目中使用到了zip进行文件的传输,可以大大减少存储空间和传输流量,于是就会涉及到zip文件的加压缩问题。下面将会详细介绍java原生的zip api。先来简单列举一下java中关于zip的api:

一、zip压缩

java通过 ZipOutputStream 对zip文件建立输出流,可以使用以下构造方法:

FileOutputStream fos = new FileOutputStream(outFile);
ZipOutputStream zos = new ZipOutputStream(fos);

建立了对zip文件的输出流之后,我们需要逐个写入需要压缩的文件,通过 ZipOutputStream 的 putNextEntry(ZipEntry e)  方法,可以在建立zip文件内建立需要写入的下一个文件的“入口”。

ZipEntry 可以调用其构造方法  ZipEntry(String name)  为要写入的文件制定名字name,该名字包含此文件相对于zip文件的子目录。

举例如下:

如果我们希望将t.txt文件打包到zip文件中,并且是打包到zip文件中的“dir”文件夹中,那么就需要指定其 ZipEntry 的构造方法的name为 “dir/t.txt”。如果是直接打包到zip文件的一级目录中,那么就只需要指定name为“t.txt”即可。

下面给出一段压缩某个文件到zip的代码:


ze = new ZipEntry(subPath);
zos.putNextEntry(ze);
bis = new BufferedInputStream(new FileInputStream(f));
while (bis.read(data, 0, byteLength) != -1) {
zos.write(data);
}
bis.close();
zos.closeEntry();



上述代码中subPath是被压缩文件在zip文件中的目录+名字,每次在打开一个 ZipEntry 之后,在写入/读取操作完成以后,都需要将该 ZipEntry 关闭。向zip文件中写入文件的方法跟普通写文件相同,使用 BufferedInputStream 进行字节流写入操作即可。如果要压缩的是目录,那么就将该目录下的文件枚举进行压缩,在 ZipEntry 的name中指定其上级目录即可。

下面给出完整的zip压缩代码:


public class ZipLoader {

static int byteLength = 1024;

public ZipLoader() {

}

/**
* 压缩文件
* @param files 需要压缩的文件/目录
* @param outFile 压缩输出地zip文件的名字
* @return 压缩是否成功
* @throws FileNotFoundException
* @throws IOException
*/
public static boolean zip(File[] files, String outFile) throws FileNotFoundException, IOException{
File file = new File(outFile);
String zipPath = file.getAbsolutePath();
zipPath = zipPath.substring(0, zipPath.length()-File.separator.length()-outFile.length());
// 创建压缩文件
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream(outFile);
ZipOutputStream zos = new ZipOutputStream(fos);
// 压缩
pack(zipPath, files, zos);

zos.flush();
zos.close();
return true;
}

/**
* 打包文件/目录
* @param srcPath zip文件的绝对路径
* @param files 要打包的文件/目录
* @param zos 已连接到zip文件的zip输入流实例
* @throws FileNotFoundException
* @throws IOException
*/
private static void pack(String srcPath, File[] files, ZipOutputStream zos) throws FileNotFoundException, IOException {
BufferedInputStream bis;
ZipEntry ze;
byte[] data = new byte[byteLength];

for (File f : files) {
// 递归压缩目录
if (f.isDirectory()) {
pack(srcPath, f.listFiles(), zos);
} else if (f.getName().indexOf(".Ds_Store") != -1) {
continue;
} else {
// 获取被压缩文件相对zip文件的路径
String subPath = f.getAbsolutePath();
int index = subPath.indexOf(srcPath);
if (index != -1) {
subPath = subPath.substring(srcPath.length()+File.separator.length());
}
// 压缩文件
ze = new ZipEntry(subPath);
zos.putNextEntry(ze);
bis = new BufferedInputStream(new FileInputStream(f));
while (bis.read(data, 0, byteLength) != -1) {
zos.write(data);
}
bis.close();
zos.closeEntry();
}
}
}
}

上述ZipLoader类中的zip方法是调用pack方法进行zip压缩的,在pack方法中,如果要压缩的文件是一个目录,那么就对该目录下的文件进行pack递归调用。


二、zip解压

ZipFile 提供了entries() 方法得到zip文件的ZipEntry枚举,然后就可以根据ZipEntry的getName() 方法得到其相对zip文件的目录及文件名,再通过 BufferedOutputStream 和 BufferedInputStream 读写文件即可。


	/**
* 解压zip文件
* @param zipFile 要解压的zip文件对象
* @param unzipFilePath 解压目的绝对路径
* @throws IOException
*/
@SuppressWarnings("unchecked")
public static void unzip(File zipFile, String unzipFilePath) throws IOException {
ZipFile zip = new ZipFile(zipFile);
Enumeration<ZipEntry> entries = (Enumeration<ZipEntry>) zip.entries();
ZipEntry ze;
String unzipEntryPath;
String unzipEntryDirPath;
int index;
File unzipEntryDir;
BufferedOutputStream bos;
BufferedInputStream bis;
byte[] data = new byte[byteLength];

// 创建解压后的文件夹
File unzipDir = new File(unzipFilePath);
if (!unzipDir.exists() || !unzipDir.isDirectory()) {
unzipDir.mkdir();
}

// 解压
while (entries.hasMoreElements()) {
// 获取下一个解压文件
ze = (ZipEntry) entries.nextElement();
unzipEntryPath = unzipFilePath + File.separator + ze.getName();
index = unzipEntryPath.lastIndexOf(File.separator);
// 获取解压文件上层目录
if (index != -1) {
unzipEntryDirPath = unzipEntryPath.substring(0, index);
} else {
unzipEntryDirPath = "";
}
// 创建解压文件上层目录
unzipEntryDir = new File(unzipEntryDirPath);
if (!unzipEntryDir.exists() || !unzipEntryDir.isDirectory()) {
unzipEntryDir.mkdir();
}
// 写出解压文件
bos = new BufferedOutputStream(new FileOutputStream(unzipEntryPath));
bis = new BufferedInputStream(zip.getInputStream(ze));
while (bis.read(data, 0, byteLength) != -1) {
bos.write(data);
}
bis.close();
bos.flush();
bos.close();
}
zip.close();
}

三、不解压直接读取zip文件内指定文件的内容

有了第二部分解压的代码以后,要读取指定文件的内容并不困难,直接上代码。

	/**
* 读取zip文件中制定文件的内容
* @param zipFile 目标zip文件对象
* @param readFileName 目标读取文件名字
* @return 文件内容
* @throws ZipException
* @throws IOException
*/
@SuppressWarnings("unchecked")
public static String getZipFileContent(File zipFile, String readFileName) throws ZipException, IOException {
StringBuilder content = new StringBuilder();
ZipFile zip = new ZipFile(zipFile);
Enumeration<ZipEntry> entries = (Enumeration<ZipEntry>) zip.entries();

ZipEntry ze;
// 枚举zip文件内的文件/
while (entries.hasMoreElements()) {
ze = entries.nextElement();
// 读取目标对象
if (ze.getName().equals(readFileName)) {
Scanner scanner = new Scanner(zip.getInputStream(ze));
while (scanner.hasNextLine()) {
content.append(scanner.nextLine());
}
scanner.close();
}
}
zip.close();

return content.toString();
}