1. Información general
En este tutorial rápido, discutiremos cómo comprimir un archivo en un archivo y cómo descomprimirlo, todo usando bibliotecas centrales proporcionadas por Java.
Estas bibliotecas centrales son parte del paquete java.util.zip , donde podemos encontrar todas las utilidades relacionadas con comprimir y descomprimir.
2. Comprima un archivo
Primero echemos un vistazo a una operación simple: comprimir un solo archivo.
Para nuestro ejemplo aquí, comprimiremos un archivo llamado test1.txt en un archivo comprimido llamado compressed.zip .
Por supuesto, primero accederemos al archivo desde el disco; echemos un vistazo:
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. Comprima varios archivos
A continuación, veamos cómo comprimir varios archivos en un archivo zip. Comprimiremos test1.txt y test2.txt en 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. Comprima un directorio
Ahora, analicemos cómo comprimir un directorio completo. Nosotros directorio zipTest en 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(); } }
Tenga en cuenta que:
- Para comprimir subdirectorios, los recorremos de forma recursiva.
- Cada vez que encontramos un directorio, agregamos su nombre al nombre ZipEntry de los descendientes para guardar la jerarquía.
- También creamos una entrada de directorio para cada directorio vacío.
5. Descomprima un archivo
Ahora descomprimamos un archivo y extraigamos su contenido.
Para este ejemplo, descomprimiremos compressed.zip en una nueva carpeta llamada unzipTest .
Echemos un vistazo:
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(); } }
Dentro del tiempo de bucle, vamos a iterar a través de cada ZipEntry cheque y primero si se trata de un directorio . Si es así, crearemos el directorio usando el método mkdirs () ; de lo contrario, continuaremos con la creación del archivo:
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(); }
Una nota aquí es que en la rama else , también estamos verificando primero si existe el directorio principal del archivo. Esto es necesario para archivos comprimidos creados en Windows, donde los directorios raíz no tienen una entrada correspondiente en el archivo zip.
Otro punto clave se puede ver en el método 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; }
Este método protege contra la escritura de archivos en el sistema de archivos fuera de la carpeta de destino. Esta vulnerabilidad se llama Zip Slip y puede leer más sobre ella aquí.
6. Conclusión
Este tutorial ilustró cómo podemos usar las bibliotecas de Java para las operaciones de comprimir y descomprimir archivos.
La implementación de estos ejemplos se puede encontrar en GitHub.