平時咱們都是使用WinZip,2345好壓等軟件來操做zip文件,java也提供了ZipOutputStream,ZipEntry等API建立和解析zip文件。html
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(); } } } }
生成的壓縮文件爲
java
每個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內置的解壓縮工具這一篇博客。code
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從新生成文件或文件夾,生成文件時要確保父文件夾已經存在。orm