最近需要用到Java的zip文件压缩与解压缩,找了一堆的资料和参考代码,没有一个能够很好的解决问题。要不就是压缩出错,要不就是压缩后的文件目录组织不正确,要不就是中文乱码,总之问题一大堆。没办法只能自己编码搞定。
java里面有个包叫java.util.zip提供zip文件压缩,但是编码的时候非常不方便。编码量太多了,通过搜索,发现apache有个包提供一些简单的方法来实现zip文件的压缩与解压缩http://ant.apache.org/。下载地址:org.apache.tools.zip
下载下来解压缩后,该包中的ant.jar里面提供了zip文件压缩与解压缩的功能代码。在项目中引用该类库。
要压缩成zip文件,涉及到的问题如下:
1.将要压缩的文件添加到zip文件包中。
2.组织好要压缩的文件目录与压缩后的zip文件目录一致。
3.文件名以及文件夹名中文字符编码。
4.解压缩zip文件,并将解压缩后的文件及文件夹的组织形式要与压缩前的一致。
在设计该类的时候,我编写了一下几个方法:
第一个zip方法,将来填入要压缩的文件或文件夹路径,然后压缩成zip文件并保存到指定位置。代码如下:
1 /**
2 * 压缩文件或者文件夹 压缩采用gb2312编码,其它编码方式可能造成文件名与文件夹名使用中文的情况下压缩后为乱码。。。
3 *
4 * @param source
5 * 要压缩的文件或者文件夹
6 * 建议使用"c:/abc"或者"c:/abc/aaa.txt"这种形式来给定压缩路径
7 * 使用"c:\\abc" 或者"c:\\abc\\aaa.txt"这种形式来给定路径的话,可能导致出现压缩和解压缩路径意外故障。。。
8 * @param zipFileName
9 * 压缩后的zip文件名称 压缩后的目录组织与windows的zip压缩的目录组织相同。
10 * 会根据压缩的目录的名称,在压缩文件夹中创建一个改名的根目录, 其它压缩的文件和文件夹都在该目录下依照原来的文件目录组织形式
11 * @throws IOException
12 * 压缩文件的过程中可能会抛出IO异常,请自行处理该异常。
13 */
14 public static void ZIP(String source, String zipFileName)
15 throws IOException {
16 ZipOutputStream zos = new ZipOutputStream(new File(zipFileName));
17
18 // 设置压缩的时候文件名编码为gb2312
19 zos.setEncoding("gb2312");
20 // System.out.println(zos.getEncoding());
21
22 File f = new File(source);
23
24 if (f.isDirectory()) {
25 // 如果直接压缩文件夹
26 ZIPDIR(source, zos, f.getName() + "/");// 此处使用/来表示目录,如果使用\\来表示目录的话,会导致压缩后的文件目录组织形式在解压缩的时候不能正确识别。
27 } else {
28 // 如果直接压缩文件
29 ZIPDIR(f.getPath(), zos, new File(f.getParent()).getName() + "/");
30 ZIPFile(f.getPath(), zos, new File(f.getParent()).getName() + "/"
31 + f.getName());
32 }
33
34 zos.closeEntry();
35 zos.close();
36 }
为了很好的解决中文编码问题,这里在创建ZipOutputStream 输出流的时候就指定其编码为gb2312,这样无论将来是中文的文件名和文件夹,都可以被正确的识别,不会出现乱码的情况。
特别提醒大家注意的是,文件的路径使用的斜杠最好是"/",如果用"\\"来表示路径的话,表面上看这是区分好了文件夹,其实在解压缩的时候zipfile对象就不能很好的识别其为文件夹,有可能将其识别为文件,导致解压出错。这里是个关键点!
第二个ZIPFile方法,专门将指定的文件压缩成为zip文件的方法。代码如下:
1 /**
2 * zip 压缩单个文件。 除非有特殊需要,否则请调用ZIP方法来压缩文件!
3 *
4 * @param sourceFileName
5 * 要压缩的原文件
6 * @param zipFileName
7 * 压缩后的文件名
8 * @param zipFileName
9 * 压缩后的文件名
10 * @throws IOException
11 * 抛出文件异常
12 */
13 public static void ZIPFile(String sourceFileName, ZipOutputStream zos,
14 String tager) throws IOException {
15 // System.out.println(tager);
16 ZipEntry ze = new ZipEntry(tager);
17 zos.putNextEntry(ze);
18
19 // 读取要压缩文件并将其添加到压缩文件中
20 FileInputStream fis = new FileInputStream(new File(sourceFileName));
21 byte[] bf = new byte[2048];
22 int location = 0;
23 while ((location = fis.read(bf)) != -1) {
24 zos.write(bf, 0, location);
25 }
26 fis.close();
27 }
说明:这里写入zip文件流的时候使用了缓存,如果不使用将导致压缩的时间过长。。。(每次从硬盘读取一个字节并写入到zip文件流中,加重了硬盘的负担。。。)
经过我的机器测试,这个缓存设置1024与2048没有太多的性能差别,但是小于1024后就有较大的区别了。所以建议设置为1024或者2048。
第三个ZIPDIR方法,专门在ZIP文件中添加文件夹的方法。代码如下:
1 /**
2 * 压缩目录。 除非有特殊需要,否则请调用ZIP方法来压缩文件!
3 *
4 * @param sourceDir
5 * 需要压缩的目录位置
6 * @param zos
7 * 压缩到的zip文件
8 * @param tager
9 * 压缩到的目标位置
10 * @throws IOException
11 * 压缩文件的过程中可能会抛出IO异常,请自行处理该异常。
12 */
13 public static void ZIPDIR(String sourceDir, ZipOutputStream zos,
14 String tager) throws IOException {
15 // System.out.println(tager);
16 ZipEntry ze = new ZipEntry(tager);
17 zos.putNextEntry(ze);
18 // 提取要压缩的文件夹中的所有文件
19 File f = new File(sourceDir);
20 File[] flist = f.listFiles();
21 if (flist != null) {
22 // 如果该文件夹下有文件则提取所有的文件进行压缩
23 for (File fsub : flist) {
24 if (fsub.isDirectory()) {
25 // 如果是目录则进行目录压缩
26 ZIPDIR(fsub.getPath(), zos, tager + fsub.getName() + "/");
27 } else {
28 // 如果是文件,则进行文件压缩
29 ZIPFile(fsub.getPath(), zos, tager + fsub.getName());
30 }
31 }
32 }
33 }
说明:这里使用了递归的方式来读取要压缩的文件夹中的下级文件夹,全部将其压到zip文件中,用来组织好将来压缩好的zip文件里面的目录形式。
第四个UnZip方法,专门用于解压缩zip文件的方法。代码如下:
1 /**
2 * 解压缩zip文件
3 *
4 * @param sourceFileName
5 * 要解压缩的zip文件
6 * @param desDir
7 * 解压缩到的目录
8 * @throws IOException
9 * 压缩文件的过程中可能会抛出IO异常,请自行处理该异常。
10 */
11 public static void UnZIP(String sourceFileName, String desDir)
12 throws IOException {
13 // 创建压缩文件对象
14 ZipFile zf = new ZipFile(new File(sourceFileName));
15
16 // 获取压缩文件中的文件枚举
17 Enumeration<ZipEntry> en = zf.getEntries();
18 int length = 0;
19 byte[] b = new byte[2048];
20
21 // 提取压缩文件夹中的所有压缩实例对象
22 while (en.hasMoreElements()) {
23 ZipEntry ze = en.nextElement();
24 // System.out.println("压缩文件夹中的内容:"+ze.getName());
25 // System.out.println("是否是文件夹:"+ze.isDirectory());
26 // 创建解压缩后的文件实例对象
27 File f = new File(desDir + ze.getName());
28 // System.out.println("解压后的内容:"+f.getPath());
29 // System.out.println("是否是文件夹:"+f.isDirectory());
30 // 如果当前压缩文件中的实例对象是文件夹就在解压缩后的文件夹中创建该文件夹
31 if (ze.isDirectory()) {
32 f.mkdirs();
33 } else {
34 // 如果当前解压缩文件的父级文件夹没有创建的话,则创建好父级文件夹
35 if (!f.getParentFile().exists()) {
36 f.getParentFile().mkdirs();
37 }
38
39 // 将当前文件的内容写入解压后的文件夹中。
40 OutputStream outputStream = new FileOutputStream(f);
41 InputStream inputStream = zf.getInputStream(ze);
42 while ((length = inputStream.read(b)) > 0)
43 outputStream.write(b, 0, length);
44
45 inputStream.close();
46 outputStream.close();
47 }
48 }
49 zf.close();
50 }
解压缩的过程并不复杂,读取zip文件,提取里面的每个zip实体,然后将其解压缩。
如果该zip实体是目录,则在解压到的文件夹中创建文件夹,如果是文件,则在指定的目录中创建文件。
这个zip压缩与解压缩代码基本完毕,写个代码测试一下效果:
1 try {
2 long starTime = 0;
3 long endTime = 0;
4
5 // 测试压缩目录
6 // ZIPDIR("c:\\A",
7 // "c:\\A.zip");
8
9 // 测试压缩单个文件
10 // ZIPFile("c:\\A.txt",
11 // "c:\\A.zip");
12
13 System.out.println("开始压缩...");
14 starTime = System.currentTimeMillis();
15 ZIP("c:\\A",
16 "c:\\A.zip");
17 endTime = System.currentTimeMillis();
18 System.out.println("压缩完毕!花费时间: " + (endTime - starTime) + " 毫秒!");
19
20 System.out.println("开始解压...");
21 starTime = System.currentTimeMillis();
22 UnZIP("c:\\A.zip",
23 "c:\\A\\");
24 endTime = System.currentTimeMillis();
25 System.out.println("解压完毕!花费时间: " + (endTime - starTime) + " 毫秒!");
26
27 } catch (Exception ex) {
28 // TODO Auto-generated catch block
29 ex.printStackTrace();
30 }