在一个android项目中使用到了zip进行文件的传输,可以大大减少存储空间和传输流量,于是就会涉及到zip文件的加压缩问题。下面将会详细介绍java原生的zip api。先来简单列举一下java中关于zip的api:
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();
}
}
}
}
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 entries = (Enumeration) 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文件中制定文件的内容
* @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 entries = (Enumeration) 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();
}