1. Gambaran keseluruhan
Dalam tutorial ringkas ini, kita akan membincangkan bagaimana memasukkan fail ke dalam arkib dan bagaimana untuk mengekstrak zip - semuanya menggunakan perpustakaan teras yang disediakan oleh Java.
Perpustakaan teras ini adalah sebahagian daripada pakej java.util.zip - di mana kita dapat mencari semua utiliti berkaitan zip dan unzip.
2. Zip Fail
Mari kita lihat operasi mudah - zip satu fail.
Sebagai contoh kami di sini kami akan memasukkan fail bernama test1.txt ke dalam arkib bernama compressed.zip .
Sudah tentu kita akan mengakses fail dari cakera terlebih dahulu - mari kita lihat:
public class ZipFile { public static void main(String[] args) throws IOException { String sourceFile = "test1.txt"; FileOutputStream fos = new FileOutputStream("compressed.zip"); ZipOutputStream zipOut = new ZipOutputStream(fos); File fileToZip = new File(sourceFile); FileInputStream fis = new FileInputStream(fileToZip); ZipEntry zipEntry = new ZipEntry(fileToZip.getName()); zipOut.putNextEntry(zipEntry); byte[] bytes = new byte[1024]; int length; while((length = fis.read(bytes)) >= 0) { zipOut.write(bytes, 0, length); } zipOut.close(); fis.close(); fos.close(); } }
3. Zip Berbilang Fail
Seterusnya, mari kita lihat cara memasukkan beberapa fail ke dalam satu fail zip. Kami akan memampatkan test1.txt dan test2.txt ke multiCompressed.zip :
public class ZipMultipleFiles { public static void main(String[] args) throws IOException { List srcFiles = Arrays.asList("test1.txt", "test2.txt"); FileOutputStream fos = new FileOutputStream("multiCompressed.zip"); ZipOutputStream zipOut = new ZipOutputStream(fos); for (String srcFile : srcFiles) { File fileToZip = new File(srcFile); FileInputStream fis = new FileInputStream(fileToZip); ZipEntry zipEntry = new ZipEntry(fileToZip.getName()); zipOut.putNextEntry(zipEntry); byte[] bytes = new byte[1024]; int length; while((length = fis.read(bytes)) >= 0) { zipOut.write(bytes, 0, length); } fis.close(); } zipOut.close(); fos.close(); } }
4. Zip Direktori
Sekarang, mari kita bincangkan cara mengikat zip keseluruhan direktori. Kami akan mengarahkan zipTest ke dirCompressed.zip :
public class ZipDirectory { public static void main(String[] args) throws IOException { String sourceFile = "zipTest"; FileOutputStream fos = new FileOutputStream("dirCompressed.zip"); ZipOutputStream zipOut = new ZipOutputStream(fos); File fileToZip = new File(sourceFile); zipFile(fileToZip, fileToZip.getName(), zipOut); zipOut.close(); fos.close(); } private static void zipFile(File fileToZip, String fileName, ZipOutputStream zipOut) throws IOException { if (fileToZip.isHidden()) { return; } if (fileToZip.isDirectory()) { if (fileName.endsWith("/")) { zipOut.putNextEntry(new ZipEntry(fileName)); zipOut.closeEntry(); } else { zipOut.putNextEntry(new ZipEntry(fileName + "/")); zipOut.closeEntry(); } File[] children = fileToZip.listFiles(); for (File childFile : children) { zipFile(childFile, fileName + "/" + childFile.getName(), zipOut); } return; } FileInputStream fis = new FileInputStream(fileToZip); ZipEntry zipEntry = new ZipEntry(fileName); zipOut.putNextEntry(zipEntry); byte[] bytes = new byte[1024]; int length; while ((length = fis.read(bytes)) >= 0) { zipOut.write(bytes, 0, length); } fis.close(); } }
Perhatikan bahawa:
- Untuk mengzip sub-direktori, kami mengulanginya secara berulang.
- Setiap kali kita mencari direktori, kita menambahkan namanya ke nama keturunan ZipEntry untuk menyimpan hierarki.
- Kami juga membuat entri direktori untuk setiap direktori kosong
5. Nyahzip Arkib
Sekarang, buka zip fail dan ekstrak kandungannya.
Untuk contoh ini, kami akan menyahzipkan compressed.zip ke dalam folder baru bernama unzipTest .
Mari kita lihat:
public class UnzipFile { public static void main(String[] args) throws IOException { String fileZip = "src/main/resources/unzipTest/compressed.zip"; File destDir = new File("src/main/resources/unzipTest"); byte[] buffer = new byte[1024]; ZipInputStream zis = new ZipInputStream(new FileInputStream(fileZip)); ZipEntry zipEntry = zis.getNextEntry(); while (zipEntry != null) { // ... } zis.closeEntry(); zis.close(); } }
Di dalam gelung sementara , kami akan melakukan rutin setiap ZipEntry dan periksa terlebih dahulu apakah itu direktori . Sekiranya ada, maka kami akan membuat direktori menggunakan kaedah mkdirs () ; jika tidak, kami akan terus membuat fail:
while (zipEntry != null) { File newFile = newFile(destDir, zipEntry); if (zipEntry.isDirectory()) { if (!newFile.isDirectory() && !newFile.mkdirs()) { throw new IOException("Failed to create directory " + newFile); } } else { // fix for Windows-created archives File parent = newFile.getParentFile(); if (!parent.isDirectory() && !parent.mkdirs()) { throw new IOException("Failed to create directory " + parent); } // write file content FileOutputStream fos = new FileOutputStream(newFile); int len; while ((len = zis.read(buffer)) > 0) { fos.write(buffer, 0, len); } fos.close(); } zipEntry = zis.getNextEntry(); }
Satu catatan di sini adalah bahawa di cawangan lain , kami juga memeriksa terlebih dahulu apakah direktori induk fail itu ada. Ini diperlukan untuk arkib yang dibuat di Windows, di mana direktori root tidak mempunyai entri yang sesuai dalam fail zip.
Titik utama lain dapat dilihat dalam kaedah newFile () :
public static File newFile(File destinationDir, ZipEntry zipEntry) throws IOException { File destFile = new File(destinationDir, zipEntry.getName()); String destDirPath = destinationDir.getCanonicalPath(); String destFilePath = destFile.getCanonicalPath(); if (!destFilePath.startsWith(destDirPath + File.separator)) { throw new IOException("Entry is outside of the target dir: " + zipEntry.getName()); } return destFile; }
Kaedah ini melindungi daripada menulis fail ke sistem fail di luar folder sasaran. Kerentanan ini dipanggil Zip Slip dan anda boleh membaca lebih lanjut mengenainya di sini.
6. Kesimpulannya
Tutorial ini menggambarkan bagaimana kita dapat menggunakan perpustakaan Java untuk operasi fail zip dan zip.
Pelaksanaan contoh-contoh ini boleh didapati di GitHub.