在一个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<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文件中制定文件的内容 * @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(); }