Komprimiranje i raspakiranje u Javi

1. Pregled

U ovom brzom vodiču razgovarat ćemo o tome kako arhivirati datoteku u arhivu i kako raspakirati arhivu - a sve to pomoću osnovnih knjižnica koje nudi Java.

Te su temeljne knjižnice dio java.util.zip paket - gdje možemo pronaći sve uslužne programe s patentnim zatvaračem i raspakiranjem.

2. Zip datoteku

Pogledajmo prvo jednostavnu operaciju - arhiviranje jedne datoteke.

Za naš primjer ovdje ćemo stvoriti datoteku pod nazivom test1.txt u arhivirano imenovano stisnuto.zip.

Prvo ćemo pristupiti datoteci s diska - pogledajmo:

javna klasa ZipFile {javna statička praznina main (String [] args) baca IOException {String sourceFile = "test1.txt"; FileOutputStream fos = novi FileOutputStream ("komprimirani.zip"); ZipOutputStream zipOut = novi ZipOutputStream (fos); File fileToZip = nova datoteka (sourceFile); FileInputStream fis = novi FileInputStream (fileToZip); ZipEntry zipEntry = novi ZipEntry (fileToZip.getName ()); zipOut.putNextEntry (zipEntry); bajt [] bajtova = novi bajt [1024]; int dužina; while ((length = fis.read (bytes))> = 0) {zipOut.write (bytes, 0, length); } zipOut.close (); fis.close (); fos.close (); }}

3. Zip više datoteka

Dalje, pogledajmo kako komprimirati više datoteka u jednu zip datoteku. Komprimirat ćemo test1.txt i test2.txt u multiCompressed.zip:

javna klasa ZipMultipleFiles {javna statička void glavna (String [] args) baca IOException {Lista srcFiles = Arrays.asList ("test1.txt", "test2.txt"); FileOutputStream fos = novi FileOutputStream ("multiCompressed.zip"); ZipOutputStream zipOut = novi ZipOutputStream (fos); for (String srcFile: srcFiles) {File fileToZip = nova datoteka (srcFile); FileInputStream fis = novi FileInputStream (fileToZip); ZipEntry zipEntry = novi ZipEntry (fileToZip.getName ()); zipOut.putNextEntry (zipEntry); bajt [] bajtova = novi bajt [1024]; int dužina; while ((length = fis.read (bytes))> = 0) {zipOut.write (bytes, 0, length); } fis.close (); } zipOut.close (); fos.close (); }}

4. Zip direktorij

Sada, razgovarajmo o tome kako komprimirati cijeli direktorij. Mi ćemo katalog zipTest u dirCompressed.zip :

javna klasa ZipDirectory {public static void main (String [] args) baca IOException {String sourceFile = "zipTest"; FileOutputStream fos = novi FileOutputStream ("dirCompressed.zip"); ZipOutputStream zipOut = novi ZipOutputStream (fos); File fileToZip = nova datoteka (sourceFile); zipFile (fileToZip, fileToZip.getName (), zipOut); zipOut.close (); fos.close (); } privatna statička praznina zipFile (File fileToZip, String fileName, ZipOutputStream zipOut) baca IOException {if (fileToZip.isHidden ()) {return; } if (fileToZip.isDirectory ()) {if (fileName.endsWith ("/")) {zipOut.putNextEntry (novi ZipEntry (fileName)); zipOut.closeEntry (); } else {zipOut.putNextEntry (novi ZipEntry (Ime datoteke + "/")); zipOut.closeEntry (); } Datoteka [] kids = fileToZip.listFiles (); za (Datoteka childFile: djeca) {zipFile (childFile, fileName + "/" + childFile.getName (), zipOut); } povratak; } FileInputStream fis = novi FileInputStream (fileToZip); ZipEntry zipEntry = novi ZipEntry (ime datoteke); zipOut.putNextEntry (zipEntry); bajt [] bajtova = novi bajt [1024]; int dužina; while ((length = fis.read (bytes))> = 0) {zipOut.write (bytes, 0, length); } fis.close (); }}

Imajte na umu da:

  • Da bismo zip arhivirali poddirektoriju, ponavljamo ih rekurzivno.
  • Svaki put kad pronađemo direktorij, potomcima dodamo njegovo ime ZipEntry ime za spremanje hijerarhije.
  • Također stvaramo unos direktorija za svaki prazan direktorij

5. Otpakirajte arhivu

Otpakirajmo sada arhivu i izdvojimo njezin sadržaj.

Za ovaj primjer raspakirat ćemo stisnuto.zip u novu mapu pod nazivom raspakirajTest.

Pogledajmo:

javna klasa UnzipFile {public static void main (String [] args) baca IOException {String fileZip = "src / main / resources / unzipTest / compressed.zip"; Datoteka destDir = nova datoteka ("src / main / resources / unzipTest"); bajt [] međuspremnik = novi bajt [1024]; ZipInputStream zis = novi ZipInputStream (novi FileInputStream (fileZip)); ZipEntry zipEntry = zis.getNextEntry (); while (zipEntry! = null) {// ...} zis.closeEntry (); zis.close (); }}

Unutar dok petlja, ponovit ćemo svaku ZipEntry i prvo provjerite je li to direktorij. Ako jest, tada ćemo stvoriti direktorij pomoću mkdirs () metoda; u suprotnom, nastavit ćemo s izradom datoteke:

while (zipEntry! = null) {Datoteka newFile = newFile (destDir, zipEntry); if (zipEntry.isDirectory ()) {if (! newFile.isDirectory () &&! newFile.mkdirs ()) {throw new IOException ("Nije uspjelo stvaranje direktorija" + newFile); }} else {// popravak za arhive kreirane u sustavu Windows. Roditelj = newFile.getParentFile (); if (! parent.isDirectory () &&! parent.mkdirs ()) {throw new IOException ("Nije uspjelo stvaranje direktorija" + roditelj); } // zapisivanje sadržaja datoteke FileOutputStream fos = new FileOutputStream (newFile); int len; while ((len = zis.read (buffer))> 0) {fos.write (međuspremnik, 0, len); } fos.close (); } zipEntry = zis.getNextEntry (); }

Jedna napomena ovdje je ona na drugo grana, također prvo provjeravamo postoji li nadređeni direktorij datoteke. To je potrebno za arhive stvorene u sustavu Windows, gdje korijenski direktoriji nemaju odgovarajući unos u zip datoteci.

Još jedna ključna točka može se vidjeti u newFile () metoda:

javna statička datoteka newFile (datoteka destinationDir, ZipEntry zipEntry) baca IOException {Datoteka destFile = nova datoteka (destinationDir, zipEntry.getName ()); Niz destDirPath = odredišteDir.getCanonicalPath (); Niz destFilePath = destFile.getCanonicalPath (); if (! destFilePath.startsWith (destDirPath + File.separator)) {throw new IOException ("Unos je izvan ciljnog direktorija:" + zipEntry.getName ()); } return destFile; }

Ova metoda štiti od pisanja datoteka u datotečni sustav izvan ciljne mape. Ova ranjivost naziva se Zip Slip i više o njoj možete pročitati ovdje.

6. Zaključak

Ovaj je vodič ilustrirao kako možemo koristiti Java knjižnice za operacije zippiranja i raspakiranja datoteka.

Implementacija ovih primjera može se naći na GitHubu.