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

在一个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 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文件内指定文件的内容

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

	/**
	 * 读取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();
	}




你可能感兴趣的:(java)