java zip_Java压缩技术(二) ZIP压缩——Java原生实现

Java压缩技术(一) ZLib),一直惦记却没时间补充。今天得空,整理一下ZIP的java原生实现。

看了几篇zip压缩算法的帖子,讲的算是比较细致了,但就是没有对应的解压缩实现,太惜败了!

32b16d55493e15c0f1e400cb49121039.gif 我就喜欢没事做总结,稍作整理,将其收纳!

966903dea4bcb507358d5dcce8b912e5.gif

相关链接:

Java压缩技术(一) ZLib

Java压缩技术(二) ZIP压缩——Java原生实现

Java压缩技术(三) ZIP解压缩——Java原生实现

Java压缩技术(四) GZIP——Java原生实现

Java压缩技术(五) GZIP相关——浏览器解析

Java压缩技术(六) BZIP2——Commons实现

Java压缩技术(七) TAR——Commons实现

查过相关资料后才知道,ZIP应该算作归档类的压缩算法,每一门学科都可深可浅!

966903dea4bcb507358d5dcce8b912e5.gif

闲言少叙,先说ZIP压缩。

zip压缩需要通过ZipOutputStream 执行write方法将压缩数据写到指定输出流中。

注意,这里应先使用CheckedOutputStream 指定文件校验算法。(通常使用CRC32算法)。代码如下所示:

CheckedOutputStream cos = new CheckedOutputStream(new FileOutputStream(destPath), new CRC32());

ZipOutputStream zos = new ZipOutputStream(cos);

接下来,需要将待压缩文件以ZipEntry的方式追加到压缩文件中,如下所示:

/**

* 压缩包内文件名定义

*

*

 
 

* 如果有多级目录,那么这里就需要给出包含目录的文件名

* 如果用WinRAR打开压缩包,中文名将显示为乱码

*

*/

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();

当然,如果待添加的压缩项是一个目录。那么,需要通过递归的方式指定最终的压缩项。

如果要添加一个空目录,注意使用符号"/"(String 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打开后,可以看到这个目录下还有一个空文件名文件!

50880c61e3199786f144db0328de5b6f.gif

e666b02f-3529-3c26-9b7a-d997fd330414.jpg

来个完整的压缩实现,代码如下所示:

/**

* 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 梁栋

* @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 {

/**

* 压缩包内文件名定义

*

*

 
 

* 如果有多级目录,那么这里就需要给出包含目录的文件名

* 如果用WinRAR打开压缩包,中文名将显示为乱码

*

*/

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;

/**

*

* @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打开看看,是不是效果几乎一致?

966903dea4bcb507358d5dcce8b912e5.gif

当然,上述代码有所不足之处主要是中文名称乱码问题。用java原生ZIP实现压缩后得到的压缩包,与系统的字符集不同,文件/目录名将出现乱码。这是所有归档压缩都会遇到的问题。对于这种问题,Commons Copress提供了解决方案!

966903dea4bcb507358d5dcce8b912e5.gif

对于解压缩,请关注后续内容!

966903dea4bcb507358d5dcce8b912e5.gif

相关链接:

Java压缩技术(一) ZLib

Java压缩技术(二) ZIP压缩——Java原生实现

Java压缩技术(三) ZIP解压缩——Java原生实现

Java压缩技术(四) GZIP——Java原生实现

Java压缩技术(五) GZIP相关——浏览器解析

Java压缩技术(六) BZIP2——Commons实现

Java压缩技术(七) TAR——Commons实现

16

8

分享到:

18e900b8666ce6f233d25ec02f95ee59.png

72dd548719f0ace4d5f9bca64e1d7715.png

2010-04-13 00:02

浏览 59599

评论

6 楼

夏季浅忆-卖小子

2017-11-27

按照你这个方法压缩出来的文件使用winRAR解压时候提示文件被损坏无法解压

5 楼

wjc19871222

2017-11-17

niubist

太牛逼了,学习了

3 楼

wzwdcld

2015-09-11

楼主这个也出书了?

1 楼

lijie1819

2012-05-25

能否将两个文件压缩在一起呢

Java实现压缩与解压缩ZIP   import java.io.BufferedInputStream;   import java.io.BufferedOutputStream;   import java.io.File;   import java.io.FileInputStream;   import java.io.FileOutputStream;   import java.util.zip.ZipEntry;   import java.util.zip.ZipOutputStream;   public class Zip {   static final int BUFFER = 2048;   public static void main(String argv[]) {   try {   BufferedInputStream origin = null;   FileOutputStream dest = new FileOutputStream("E:\\test\\myfiles.zip");   ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(   dest));   byte data[] = new byte[BUFFER];   File f = new File("e:\\test\\a\\");   File files[] = f.listFiles();   for (int i = 0; i < files.length; i++) {   FileInputStream fi = new FileInputStream(files[i]);   origin = new BufferedInputStream(fi, BUFFER);   ZipEntry entry = new ZipEntry(files[i].getName());   out.putNextEntry(entry);   int count;   while ((count = origin.read(data, 0, BUFFER)) != -1) {   out.write(data, 0, count);   }   origin.close();   }   out.close();   } catch (Exception e) {   e.printStackTrace();   }   }   }   解压缩的   import java.io.BufferedInputStream;   import java.io.BufferedOutputStream;   import java.io.File;   import java.io.FileOutputStream;   import java.util.Enumeration;   import java.util.zip.ZipEntry;   import java.util.zip.ZipFile;   public class UnZip {   static final int BUFFER = 2048;   public static void main(String argv[]) {   try {   String fileName = "E:\\test\\myfiles.zip";   String filePath = "E:\\test\\";   ZipFile zipFile = new ZipFile(fileName);   Enumeration emu = zipFile.entries();   int i=0;   while(emu.hasMoreElements()){   ZipEntry entry = (ZipEntry)emu.nextElement();   //会把目录作为一个file读出一次,所以只建立目录就可以,之下的文件还会被迭代到。   if (entry.isDirectory())   {   new File(filePath + entry.getName()).mkdirs();   continue;   }   BufferedInputStream bis = new BufferedInputStream(zipFile.getInputStream(entry));   File file = new File(filePath + entry.getName());   //加入这个的原因是zipfile读取文件是随机读取的,这就造成可能先读取一个文件   //而这个文件所在的目录还没有出现过,所以要建出目录来。   File parent = file.getParentFile();   if(parent != null && (!parent.exists())){   parent.mkdirs();   }   FileOutputStream fos = new FileOutputStream(file);   BufferedOutputStream bos = new BufferedOutputStream(fos,BUFFER);   int count;   byte data[] = new byte[BUFFER];   while ((count = bis.read(data, 0, BUFFER)) != -1)   {   bos.write(data, 0, count);   }   bos.flush();   bos.close();   bis.close();   }   zipFile.close();   } catch (Exception e) {   e.printStackTrace();   }   }   }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值