java操作zip檔案
阿新 • • 發佈:2020-12-13
簡介
平時我們都是使用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重新生成檔案或資料夾,生成檔案時要確保父資料夾已經存在。