java操作zip文件

简介

平时我们都是使用WinZip,2345好压等软件来操作zip文件,java也提供了ZipOutputStream,ZipEntry等API创建和解析zip文件。

压缩

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.util.Objects;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class Client {

  public static void main(String[] args) {
    compressFileToZip("D:\\original_compute\\sku-20140802",
        "D:\\original_compute\\sku-20140802.zip");
  }

  /**
   * 读取文件内容并压缩,既支持文件也支持文件夹
   *
   * @param filePath 文件路径
   */
  private static void compressFileToZip(String filePath,
      String zipFilePath) {
    try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFilePath))) {
      //递归的压缩文件夹和文件
      doCompress("", filePath, zos);
      //必须
      zos.finish();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  private static void doCompress(String parentFilePath, String filePath, ZipOutputStream zos) {
    File sourceFile = new File(filePath);
    if (!sourceFile.exists()) {
      return;
    }
    String zipEntryName = parentFilePath + "/" + sourceFile.getName();
    if (parentFilePath.isEmpty()) {
      zipEntryName = sourceFile.getName();
    }
    if (sourceFile.isDirectory()) {
      File[] childFiles = sourceFile.listFiles();
      if (Objects.isNull(childFiles)) {
        return;
      }
      for (File childFile : childFiles) {
        doCompress(zipEntryName, childFile.getAbsolutePath(), zos);
      }
    } else {
      int len = -1;
      byte[] buf = new byte[1024];
      try (InputStream input = new BufferedInputStream(new FileInputStream(sourceFile))) {
        zos.putNextEntry(new ZipEntry(zipEntryName));
        while ((len = input.read(buf)) != -1) {
          zos.write(buf, 0, len);
        }
      } catch (Exception e) {
        e.printStackTrace();
      }
    }
  }
}

生成的压缩文件为

每一个ZipEntry表示一个压缩子文件,如sku.html。注意,ZipEntry的name必须为目录名+文件名,如sku-20140802/sku/sku.html。

/**
 * This class implements an output stream filter for writing files in the
 * ZIP file format. Includes support for both compressed and uncompressed
 * entries.
 *
 * @author      David Connelly
 * @since 1.1
 */
public
class ZipOutputStream extends DeflaterOutputStream implements ZipConstants {

    /**
     * 在写入文件内容之前写入一些zip格式相关的数据
     */
    public void putNextEntry(ZipEntry e) throws IOException {
        ensureOpen();
        if (current != null) {
            closeEntry();       // close previous entry
        }
        if (e.xdostime == -1) {
            // by default, do NOT use extended timestamps in extra
            // data, for now.
            e.setTime(System.currentTimeMillis());
        }
        if (e.method == -1) {
            e.method = method;  // use default method
        }
        // store size, compressed size, and crc-32 in LOC header
        e.flag = 0;
        switch (e.method) {
        case DEFLATED:
            // store size, compressed size, and crc-32 in data descriptor
            // immediately following the compressed entry data
            if (e.size  == -1 || e.csize == -1 || e.crc   == -1)
                e.flag = 8;

            break;
        case STORED:
            // compressed size, uncompressed size, and crc-32 must all be
            // set for entries using STORED compression method
            if (e.size == -1) {
                e.size = e.csize;
            } else if (e.csize == -1) {
                e.csize = e.size;
            } else if (e.size != e.csize) {
                throw new ZipException(
                    "STORED entry where compressed != uncompressed size");
            }
            if (e.size == -1 || e.crc == -1) {
                throw new ZipException(
                    "STORED entry missing size, compressed size, or crc-32");
            }
            break;
        default:
            throw new ZipException("unsupported compression method");
        }
        if (! names.add(e.name)) {
            throw new ZipException("duplicate entry: " + e.name);
        }
        if (zc.isUTF8())
            e.flag |= USE_UTF8;
        current = new XEntry(e, written);
        xentries.add(current);
        writeLOC(current);
    }

    /**
     * 文件内容写入
     */
    public synchronized void write(byte[] b, int off, int len)
        throws IOException
    {
        ensureOpen();
        if (off < 0 || len < 0 || off > b.length - len) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return;
        }

        if (current == null) {
            throw new ZipException("no current ZIP entry");
        }
        ZipEntry entry = current.entry;
        switch (entry.method) {
        case DEFLATED:
            super.write(b, off, len);
            break;
        case STORED:
            written += len;
            if (written - locoff > entry.size) {
                throw new ZipException(
                    "attempt to write past end of STORED entry");
            }
            out.write(b, off, len);
            break;
        default:
            throw new ZipException("invalid compression method");
        }
        crc.update(b, off, len);
    }

    /**
     * 文件内容写入之后写入zip格式相关的数据
     */
    public void finish() throws IOException {
        ensureOpen();
        if (finished) {
            return;
        }
        if (current != null) {
            closeEntry();
        }
        // write central directory
        long off = written;
        for (XEntry xentry : xentries)
            writeCEN(xentry);
        writeEND(off, written - off);
        finished = true;
    }
}

可以看到,ZipOutputStream也是扩展DeflaterOutputStream,不了解Deflater的可以看java内置的解压缩工具这一篇博客。

解压

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;

public class Client {

  public static void main(String[] args) throws Exception {
    decompressFromZip("D:/original_compute/sku-20140802.zip",
        "D:/original_compute/testzip/");
  }

  /**
   * 文件解压缩,支持文件和文件夹的解压
   *
   * @param zipFilePath 压缩包路径
   * @param destFilePath 解压路径
   */
  private static void decompressFromZip(String zipFilePath, String destFilePath) {
    File file = new File(zipFilePath);
    try (ZipFile zipFile = new ZipFile(file);
        ZipInputStream zis = new ZipInputStream(new FileInputStream(file))) {
      ZipEntry zipEntry = null;
      while ((zipEntry = zis.getNextEntry()) != null) {
        String fileName = destFilePath + "/" + zipEntry.getName();
        File entryFile = new File(fileName);
        if (zipEntry.isDirectory()) {
          //创建文件夹
          entryFile.mkdir();
        } else {
          //创建文件之前必须保证父文件夹存在
          if (!entryFile.getParentFile().exists()) {
            entryFile.getParentFile().mkdirs();
          }
          //创建文件
          entryFile.createNewFile();
        }
        try (InputStream input = zipFile.getInputStream(zipEntry);
            OutputStream output = new FileOutputStream(entryFile)) {
          int len = -1;
          byte[] buf = new byte[1024];
          while ((len = input.read(buf)) != -1) {
            output.write(buf, 0, len);
          }
        }
      }
    } catch (Exception e) {
      e.printStackTrace();
    }

  }
}

解压主要就是要获取到所有的压缩子文件,就是ZipEntry,将每一个ZipEntry重新生成文件或文件夹,生成文件时要确保父文件夹已经存在。

posted @ 2020-12-13 11:12  strongmore  阅读(802)  评论(0编辑  收藏  举报