去年整理了一篇ZLib算法Java实现(
Java压缩技术(一) ZLib),一直惦记却没时间补充。今天得空,整理一下ZIP的java原生实现。
看了几篇zip压缩算法的帖子,讲的算是比较细致了,但就是没有对应的解压缩实现,太惜败了!
我就喜欢没事做总结,稍作整理,将其收纳!
相关链接:
Java压缩技术(一) ZLib
Java压缩技术(二) ZIP压缩——Java原生实现
Java压缩技术(三) ZIP解压缩——Java原生实现
Java压缩技术(四) GZIP——Java原生实现
Java压缩技术(五) GZIP相关——浏览器解析
Java压缩技术(六) BZIP2——Commons实现
Java压缩技术(七) TAR——Commons实现
查过相关资料后才知道,ZIP应该算作归档类的压缩算法,每一门学科都可深可浅!
闲言少叙,先说ZIP压缩。
zip压缩需要通过ZipOutputStream 执行write方法将压缩数据写到指定输出流中。
注意,这里应先使用CheckedOutputStream 指定文件校验算法。(通常使用CRC32算法)。代码如下所示:
- CheckedOutputStream cos = new CheckedOutputStream(new FileOutputStream(destPath), new CRC32());
- ZipOutputStream zos = new ZipOutputStream(cos);
CheckedOutputStream cos = new CheckedOutputStream(new FileOutputStream(destPath), new CRC32());
ZipOutputStream zos = new ZipOutputStream(cos);
接下来,需要将待压缩文件以ZipEntry的方式追加到压缩文件中,如下所示:
-
-
-
-
-
-
-
-
- ZipEntry entry = new ZipEntry(dir + file.getName());
-
- zos.putNextEntry(entry);
/**
* 压缩包内文件名定义
*
* <pre>
* 如果有多级目录,那么这里就需要给出包含目录的文件名
* 如果用WinRAR打开压缩包,中文名将显示为乱码
* </pre>
*/
ZipEntry entry = new ZipEntry(dir + file.getName());
zos.putNextEntry(entry);
ZipEntry就是压缩包中的每一个实体!
完成上述准备后,就可以执行压缩操作了。实际上,就是执行ZipOutputStream类的write方法,如下所示:
- BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
- file));
-
- int count;
- byte data[] = new byte[BUFFER];
- while ((count = bis.read(data, 0, BUFFER)) != -1) {
- zos.write(data, 0, count);
- }
- bis.close();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
file));
int count;
byte data[] = new byte[BUFFER];
while ((count = bis.read(data, 0, BUFFER)) != -1) {
zos.write(data, 0, count);
}
bis.close();
当然,如果待添加的压缩项是一个目录。那么,需要通过递归的方式指定最终的压缩项。
如果要添加一个空目录,注意使用符号"/"(String PATH="/";)作为添加项名字结尾符!
递归构建目录压缩,代码如下:
-
-
-
-
-
-
-
-
-
-
-
- private static void compress(File srcFile, ZipOutputStream zos,
- String basePath) throws Exception {
- if (srcFile.isDirectory()) {
- compressDir(srcFile, zos, basePath);
- } else {
- compressFile(srcFile, zos, basePath);
- }
- }
-
-
-
-
-
-
-
-
-
- private static void compressDir(File dir, ZipOutputStream zos,
- String basePath) throws Exception {
-
- File[] files = dir.listFiles();
-
-
- if (files.length < 1) {
- ZipEntry entry = new ZipEntry(basePath + dir.getName() + PATH);
-
- zos.putNextEntry(entry);
- zos.closeEntry();
- }
-
- for (File file : files) {
-
- compress(file, zos, basePath + dir.getName() + PATH);
- }
- }
/**
* 压缩
*
* @param srcFile
* 源路径
* @param zos
* ZipOutputStream
* @param basePath
* 压缩包内相对路径
* @throws Exception
*/
private static void compress(File srcFile, ZipOutputStream zos,
String basePath) throws Exception {
if (srcFile.isDirectory()) {
compressDir(srcFile, zos, basePath);
} else {
compressFile(srcFile, zos, basePath);
}
}
/**
* 压缩目录
*
* @param dir
* @param zos
* @param basePath
* @throws Exception
*/
private static void compressDir(File dir, ZipOutputStream zos,
String basePath) throws Exception {
File[] files = dir.listFiles();
// 构建空目录
if (files.length < 1) {
ZipEntry entry = new ZipEntry(basePath + dir.getName() + PATH);
zos.putNextEntry(entry);
zos.closeEntry();
}
for (File file : files) {
// 递归压缩
compress(file, zos, basePath + dir.getName() + PATH);
}
}
x是一个空目录,用WinRAR打开后,可以看到这个目录下还有一个空文件名文件!
来个完整的压缩实现,代码如下所示:
-
-
-
- package org.zlex.commons.io;
-
- import java.io.BufferedInputStream;
- import java.io.BufferedOutputStream;
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileOutputStream;
- import java.util.zip.CRC32;
- import java.util.zip.CheckedInputStream;
- import java.util.zip.CheckedOutputStream;
- import java.util.zip.ZipEntry;
- import java.util.zip.ZipInputStream;
- import java.util.zip.ZipOutputStream;
-
-
-
-
-
-
-
- public class ZipUtils {
-
- public static final String EXT = ".zip";
- private static final String BASE_DIR = "";
-
-
- private static final String PATH = "/";
- private static final int BUFFER = 1024;
-
-
-
-
-
-
-
- public static void compress(File srcFile) throws Exception {
- String name = srcFile.getName();
- String basePath = srcFile.getParent();
- String destPath = basePath + name + EXT;
- compress(srcFile, destPath);
- }
-
-
-
-
-
-
-
-
-
-
- public static void compress(File srcFile, File destFile) throws Exception {
-
-
- CheckedOutputStream cos = new CheckedOutputStream(new FileOutputStream(
- destFile), new CRC32());
-
- ZipOutputStream zos = new ZipOutputStream(cos);
-
- compress(srcFile, zos, BASE_DIR);
-
- zos.flush();
- zos.close();
- }
-
-
-
-
-
-
-
-
- public static void compress(File srcFile, String destPath) throws Exception {
- compress(srcFile, new File(destPath));
- }
-
-
-
-
-
-
-
-
-
-
-
-
- private static void compress(File srcFile, ZipOutputStream zos,
- String basePath) throws Exception {
- if (srcFile.isDirectory()) {
- compressDir(srcFile, zos, basePath);
- } else {
- compressFile(srcFile, zos, basePath);
- }
- }
-
-
-
-
-
-
-
- public static void compress(String srcPath) throws Exception {
- File srcFile = new File(srcPath);
-
- compress(srcFile);
- }
-
-
-
-
-
-
-
-
-
-
- public static void compress(String srcPath, String destPath)
- throws Exception {
- File srcFile = new File(srcPath);
-
- compress(srcFile, destPath);
- }
-
-
-
-
-
-
-
-
-
- private static void compressDir(File dir, ZipOutputStream zos,
- String basePath) throws Exception {
-
- File[] files = dir.listFiles();
-
-
- if (files.length < 1) {
- ZipEntry entry = new ZipEntry(basePath + dir.getName() + PATH);
-
- zos.putNextEntry(entry);
- zos.closeEntry();
- }
-
- for (File file : files) {
-
-
- compress(file, zos, basePath + dir.getName() + PATH);
-
- }
- }
-
-
-
-
-
-
-
-
-
-
-
-
- private static void compressFile(File file, ZipOutputStream zos, String dir)
- throws Exception {
-
-
-
-
-
-
-
-
-
- ZipEntry entry = new ZipEntry(dir + file.getName());
-
- zos.putNextEntry(entry);
-
- BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
- file));
-
- int count;
- byte data[] = new byte[BUFFER];
- while ((count = bis.read(data, 0, BUFFER)) != -1) {
- zos.write(data, 0, count);
- }
- bis.close();
-
- zos.closeEntry();
- }
-
- }
/**
* 2010-4-12
*/
package org.zlex.commons.io;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.zip.CRC32;
import java.util.zip.CheckedInputStream;
import java.util.zip.CheckedOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
/**
* ZIP压缩工具
*
* @author <a href="mailto:[email protected]">梁栋</a>
* @since 1.0
*/
public class ZipUtils {
public static final String EXT = ".zip";
private static final String BASE_DIR = "";
// 符号"/"用来作为目录标识判断符
private static final String PATH = "/";
private static final int BUFFER = 1024;
/**
* 压缩
*
* @param srcFile
* @throws Exception
*/
public static void compress(File srcFile) throws Exception {
String name = srcFile.getName();
String basePath = srcFile.getParent();
String destPath = basePath + name + EXT;
compress(srcFile, destPath);
}
/**
* 压缩
*
* @param srcFile
* 源路径
* @param destPath
* 目标路径
* @throws Exception
*/
public static void compress(File srcFile, File destFile) throws Exception {
// 对输出文件做CRC32校验
CheckedOutputStream cos = new CheckedOutputStream(new FileOutputStream(
destFile), new CRC32());
ZipOutputStream zos = new ZipOutputStream(cos);
compress(srcFile, zos, BASE_DIR);
zos.flush();
zos.close();
}
/**
* 压缩文件
*
* @param srcFile
* @param destPath
* @throws Exception
*/
public static void compress(File srcFile, String destPath) throws Exception {
compress(srcFile, new File(destPath));
}
/**
* 压缩
*
* @param srcFile
* 源路径
* @param zos
* ZipOutputStream
* @param basePath
* 压缩包内相对路径
* @throws Exception
*/
private static void compress(File srcFile, ZipOutputStream zos,
String basePath) throws Exception {
if (srcFile.isDirectory()) {
compressDir(srcFile, zos, basePath);
} else {
compressFile(srcFile, zos, basePath);
}
}
/**
* 压缩
*
* @param srcPath
* @throws Exception
*/
public static void compress(String srcPath) throws Exception {
File srcFile = new File(srcPath);
compress(srcFile);
}
/**
* 文件压缩
*
* @param srcPath
* 源文件路径
* @param destPath
* 目标文件路径
*
*/
public static void compress(String srcPath, String destPath)
throws Exception {
File srcFile = new File(srcPath);
compress(srcFile, destPath);
}
/**
* 压缩目录
*
* @param dir
* @param zos
* @param basePath
* @throws Exception
*/
private static void compressDir(File dir, ZipOutputStream zos,
String basePath) throws Exception {
File[] files = dir.listFiles();
// 构建空目录
if (files.length < 1) {
ZipEntry entry = new ZipEntry(basePath + dir.getName() + PATH);
zos.putNextEntry(entry);
zos.closeEntry();
}
for (File file : files) {
// 递归压缩
compress(file, zos, basePath + dir.getName() + PATH);
}
}
/**
* 文件压缩
*
* @param file
* 待压缩文件
* @param zos
* ZipOutputStream
* @param dir
* 压缩文件中的当前路径
* @throws Exception
*/
private static void compressFile(File file, ZipOutputStream zos, String dir)
throws Exception {
/**
* 压缩包内文件名定义
*
* <pre>
* 如果有多级目录,那么这里就需要给出包含目录的文件名
* 如果用WinRAR打开压缩包,中文名将显示为乱码
* </pre>
*/
ZipEntry entry = new ZipEntry(dir + file.getName());
zos.putNextEntry(entry);
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(
file));
int count;
byte data[] = new byte[BUFFER];
while ((count = bis.read(data, 0, BUFFER)) != -1) {
zos.write(data, 0, count);
}
bis.close();
zos.closeEntry();
}
}
来做个简单的测试:
- import static org.junit.Assert.*;
-
- import org.junit.Test;
-
-
-
-
-
-
-
- public class ZipUtilsTest {
-
-
-
-
- @Test
- public void test() throws Exception {
-
- ZipUtils.compress("d:\\f.txt");
-
- ZipUtils.compress("d:\\fd");
- }
- }
import static org.junit.Assert.*;
import org.junit.Test;
/**
*
* @author 梁栋
* @version 1.0
* @since 1.0
*/
public class ZipUtilsTest {
/**
*
*/
@Test
public void test() throws Exception {
// 压缩文件
ZipUtils.compress("d:\\f.txt");
// 压缩目录
ZipUtils.compress("d:\\fd");
}
}
现在用WinRAR打开看看,是不是效果几乎一致?
当然,上述代码有所不足之处主要是中文名称乱码问题。用java原生ZIP实现压缩后得到的压缩包,与系统的字符集不同,文件/目录名将出现乱码。这是所有归档压缩都会遇到的问题。对于这种问题,Commons Copress提供了解决方案!
对于解压缩,请关注后续内容!
相关链接:
Java压缩技术(一) ZLib
Java压缩技术(二) ZIP压缩——Java原生实现
Java压缩技术(三) ZIP解压缩——Java原生实现
Java压缩技术(四) GZIP——Java原生实现
Java压缩技术(五) GZIP相关——浏览器解析
Java压缩技术(六) BZIP2——Commons实现
Java压缩技术(七) TAR——Commons实现