Zip是常用的无损压缩算法实现,Java中提供了Zip的实现,本文针对磁盘上和内存中两种方式进行压缩和解压演示,演示只针对一层目录结构进行,多层目录只需递归操作进行即可。
· Maven依赖
<dependency>
<groupId>ant</groupId>
<artifactId>ant</artifactId>
<version>1.8.0</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
· 磁盘压缩和解压
一般情况下,都是在磁盘上对文件进行操作,将所有文件存放在某一目录中,然后对目录进行压缩,工具类代码如下:
package com.arhorchin.securitit.compress.zip;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.InflaterInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
import org.apache.commons.io.FileUtils;
public class ZipDiskUtil {
/**
* 检索文件长度.
*/
private static Integer lengthLimit = 3;
/**
* GBK字符集.
*/
public static String GBK_CHARSET = "GBK";
/**
* ZIP文件压缩.
* @param srcDirPath 待压缩文件目录.
* @param tarFilePath 压缩后文件路径.
* @return zip文件内容.
* @throws Exception .
*/
public static void zipCompress(String srcDirPath, String tarFilePath) throws Exception {
// 1.压缩文件变量.
FileOutputStream fos = null;
ZipOutputStream zos = null;
InputStream is = null;
File srcDirFile = null;
File[] filesArr = null;
try {
srcDirFile = new File(srcDirPath);
fos = new FileOutputStream(new File(tarFilePath));
filesArr = srcDirFile.listFiles();
// 2.压缩文件变量初始化.
zos = new ZipOutputStream(fos);
// 3.压缩列表文件.
for (File file : filesArr) {
zos.putNextEntry(new ZipEntry(file.getName()));
int len;
byte[] buf = new byte[1024];
is = new FileInputStream(file);
while ((len = is.read(buf)) != -1) {
zos.write(buf, 0, len);
}
}
} finally {
if (null != is)
is.close();
if (null != zos)
zos.close();
if (null != fos)
fos.close();
}
}
/**
* ZIP文件压缩.低版本JDK时,解决条目中文文件名乱码问题.
* @param srcDirPath 待压缩文件目录.
* @param tarFilePath 压缩后文件路径.
* @return zip文件内容.
* @throws Exception .
*/
public static void zipCompressCn(String srcDirPath, String tarFilePath) throws Exception {
// 1.压缩文件变量.
org.apache.tools.zip.ZipOutputStream zos = null;
InputStream is = null;
FileOutputStream fos = null;
File srcDirFile = null;
File[] filesArr = null;
try {
srcDirFile = new File(srcDirPath);
filesArr = srcDirFile.listFiles();
// 2.压缩文件变量初始化.
fos = new FileOutputStream(new File(tarFilePath));
zos = new org.apache.tools.zip.ZipOutputStream(fos);
zos.setEncoding(GBK_CHARSET);
// 3.压缩列表文件.
for (File file : filesArr) {
zos.putNextEntry(new org.apache.tools.zip.ZipEntry((file.getName())));
int len;
byte[] buf = new byte[1024];
is = new FileInputStream(file);
while ((len = is.read(buf)) != -1) {
zos.write(buf, 0, len);
}
}
} finally {
if (null != is)
is.close();
if (null != fos)
fos.close();
if (null != zos)
zos.close();
}
}
/**
* ZIP文件解压.
* @param srcDirPath 待压缩文件目录.
* @param tarFilePath 压缩后文件路径.
* @return zip文件内容.
* @throws Exception .
*/
public static void zipDecompress(String srcFilePath, String tarDirPath) throws Exception {
// 1.解压文件变量.
FileInputStream fis = null;
BufferedInputStream bis = null;
ZipInputStream zis = null;
String tmpFileName = null;
try {
// 2.解压文件初始化.
fis = new FileInputStream(new File(srcFilePath));
bis = new BufferedInputStream(fis);
zis = new ZipInputStream(bis);
ZipEntry entry;
// 3.搜索文件操作.
while ((entry = zis.getNextEntry()) != null) {
tmpFileName = entry.getName();
if (tmpFileName.length() <= lengthLimit) {
continue;
}
FileUtils.writeByteArrayToFile(new File(tarDirPath + File.separator + tmpFileName), getEntryByte(zis));
}
} finally {
if (null != fis)
fis.close();
if (null != zis)
zis.close();
if (null != bis)
bis.close();
}
}
/**
* 获取条目byte[]字节 .
* @param zis .
* @return 条目字节.
*/
public static byte[] getEntryByte(InflaterInputStream zis) {
ByteArrayOutputStream bout = null;
try {
bout = new ByteArrayOutputStream();
byte[] temp = new byte[1024];
byte[] buf = null;
int length = 0;
while ((length = zis.read(temp, 0, 1024)) != -1) {
bout.write(temp, 0, length);
}
buf = bout.toByteArray();
return buf;
} catch (IOException e) {
return null;
} finally {
try {
if (null != bout) {
bout.close();
}
} catch (Exception e) {
return null;
}
}
}
}
测试类代码如下:
package com.arhorchin.securitit.com.compress;
import com.arhorchin.securitit.compress.zip.ZipDiskUtil;
public class ZipDiskUtilTester {
public static void main(String[] args) throws Exception {
ZipDiskUtil.zipCompress("C:/Users/Administrator/Downloads/个人文件/2020-07-13/files", "C:/Users/Administrator/Downloads/个人文件/2020-07-13/disk.zip");
ZipDiskUtil.zipDecompress("C:/Users/Administrator/Downloads/个人文件/2020-07-13/disk.zip", "C:/Users/Administrator/Downloads/个人文件/2020-07-13/disk");
}
}
· 内存压缩和解压
在实际应用中,对应不同需求,可能需要生成若干文件,然后将其压缩。在某些应用中,文件较小、文件数量较少且较为固定,频繁与磁盘操作,会带来不必要的效率影响。此时,可以在内存中将文件进行压缩得到Zip文件,工具类代码如下:
package com.arhorchin.securitit.compress.zip;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.InflaterInputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
public class ZipMemoryUtil {
/**
* 检索文件长度.
*/
private static Integer lengthLimit = 3;
/**
* GBK字符集.
*/
public static String GBK_CHARSET = "GBK";
/**
* ZIP文件压缩.
* @param fileBytesMap 文件集合.
* @return zip文件内容.
* @throws Exception .
*/
public static byte[] zipCompress(Map<String, byte[]> fileBytesMap) throws Exception {
// 1.压缩文件变量.
ByteArrayOutputStream baos = null;
ZipOutputStream zos = null;
try {
// 2.压缩文件变量初始化.
baos = new ByteArrayOutputStream();
zos = new ZipOutputStream(baos);
// 3.压缩列表文件.
if (null != fileBytesMap && fileBytesMap.size() > 0) {
for (Map.Entry<String, byte[]> file : fileBytesMap.entrySet()) {
zos.putNextEntry(new org.apache.tools.zip.ZipEntry(file.getKey()));
zos.write(file.getValue());
}
} else {
baos = null;
}
} finally {
if (null != baos)
baos.close();
if (null != zos)
zos.close();
}
if (null == baos) {
return null;
}
return baos.toByteArray();
}
/**
* ZIP包转换,将ZIP包中指定类型文件重新打包. 支持中文目录版本,依赖ANT包,使用需谨慎.
* @param fileBytesMap 文件集合.
* @return zip文件内容.
* @throws Exception .
*/
public static byte[] zipCompressCn(Map<String, byte[]> fileBytesMap) throws Exception {
// 1.压缩文件变量.
ByteArrayOutputStream baos = null;
org.apache.tools.zip.ZipOutputStream zos = null;
try {
// 2.压缩文件变量初始化.
baos = new ByteArrayOutputStream();
zos = new org.apache.tools.zip.ZipOutputStream(baos);
zos.setEncoding(GBK_CHARSET);
// 3.压缩列表文件.
if (null != fileBytesMap && fileBytesMap.size() > 0) {
for (Map.Entry<String, byte[]> file : fileBytesMap.entrySet()) {
zos.putNextEntry(new org.apache.tools.zip.ZipEntry(file.getKey()));
zos.write(file.getValue());
}
} else {
baos = null;
}
} finally {
if (null != baos)
baos.close();
if (null != zos)
zos.close();
}
if (null == baos) {
return null;
}
return baos.toByteArray();
}
/**
* ZIP文件解压.
* @param fileBytes zip数据.
* @return 解压后的文件夹内容.
* @throws Exception .
*/
public static Map<String, byte[]> zipDecompress(byte[] fileBytes) throws Exception {
// 1.解压文件变量.
Map<String, byte[]> searchFileMap = null;
byte[] searchFileBytes = null;
ByteArrayInputStream bais = null;
BufferedInputStream bis = null;
ZipInputStream zis = null;
String tmpFileName = null;
try {
// 2.解压文件初始化.
searchFileMap = new HashMap<String, byte[]>();
bais = new ByteArrayInputStream(fileBytes);
bis = new BufferedInputStream(bais);
zis = new ZipInputStream(bis);
ZipEntry entry;
// 3.搜索文件操作.
while ((entry = zis.getNextEntry()) != null) {
searchFileBytes = null;
tmpFileName = entry.getName();
if (tmpFileName.length() <= lengthLimit) {
continue;
}
searchFileBytes = getEntryByte(zis);
searchFileMap.put(tmpFileName, searchFileBytes);
}
} finally {
if (null != zis)
zis.close();
if (null != bis)
bis.close();
if (null != bais)
bais.close();
}
return searchFileMap;
}
/**
* 获取条目byte[]字节 .
* @param zis .
* @return 条目字节.
*/
public static byte[] getEntryByte(InflaterInputStream zis) {
ByteArrayOutputStream bout = null;
try {
bout = new ByteArrayOutputStream();
byte[] temp = new byte[1024];
byte[] buf = null;
int length = 0;
while ((length = zis.read(temp, 0, 1024)) != -1) {
bout.write(temp, 0, length);
}
buf = bout.toByteArray();
return buf;
} catch (IOException e) {
return null;
} finally {
try {
if (null != bout) {
bout.close();
}
} catch (Exception e) {
return null;
}
}
}
}
测试类代码如下:
package com.arhorchin.securitit.com.compress;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import com.arhorchin.securitit.compress.zip.ZipMemoryUtil;
public class ZipMemoryUtilTester {
public static void main(String[] args) throws Exception {
Map<String, byte[]> fileBytesMap = null;
fileBytesMap = new HashMap<String, byte[]>();
// 设置文件列表.
File dirFile = new File("C:/Users/Administrator/Downloads/个人文件/2020-07-13/files");
for (File file : dirFile.listFiles()) {
fileBytesMap.put(file.getName(), FileUtils.readFileToByteArray(file));
}
byte[] memoryBytes = ZipMemoryUtil.zipCompress(fileBytesMap);
FileUtils.writeByteArrayToFile(new File("C:/Users/Administrator/Downloads/个人文件/2020-07-13/memory.zip"), memoryBytes);
fileBytesMap = ZipMemoryUtil.zipDecompress(memoryBytes);
System.out.println(fileBytesMap.size());
}
}
· 总结
1) 在小文件、文件数量较小且较为固定时,提倡使用内存压缩和解压方式。使用内存换时间,减少频繁的磁盘操作。
2) 在大文件、文件数量较大时,提倡使用磁盘压缩和解压方式。过大文件对服务会造成过度的负载,磁盘压缩和解压可以缓解这种压力。
3) JDK1.6及以下版本中,JDK提供的org.apache.tools.zip.*进行压缩时,对于中文条目名称处理会出现乱码,需要额外引用Ant包,使用其org.apache.tools.zip.*来解决这个问题。