- Working with Zip and GZip files in Java
- Questions:
- 3 Answers 3
- Zipping files with java
- ZipOutputStream. Запись архивов
- Чтение архивов. ZipInputStream
- Java Zip and Unzip Files and Folders
- How to Zip Files in Java?
- How to zip files and folders in Java?
- 3 Answers 3
- Zipping and Unzipping in Java
- 1. Zip a File
- 2. Zip Multiple Files / Directories Java8
- 2. Zip Multiple Files / Directories Java7
- 3. Unzip Directory
Working with Zip and GZip files in Java
The input files might be compressed and archived more than once. For example, the «full extraction» should take any of the following inputs (I’m not in control of these), and leave behind foo.txt :
- foo.txt.gz
- foo.txt.zip
- foo.txt.gz.zip
- foo.txt.zip.gz
- .
- foo.txt.gz.gz.gz.zip.gz.zip.zip.gz.gz
- .
Then, I might be left with foo.txt , bar.mp3 , baz.exe — so I would just add them all to a new zip file with some generic name.
Questions:
- With file size being a potential concern, which (interfaces/classes/methods) should I use to quickly:
- extract zip files?
- extract gzip files?
- write zip files?
3 Answers 3
Don’t hold all this uncompressed data in memory, or you might run out of heap space. You need to stream the data out to file when uncompressing and then stream it back in from file when you want to create your final zip file.
I haven’t done zipped files before, but here is an example which shows how to uncompress a gzipped file:
import java.io.*; import java.util.zip.*; //unzipping a gzipped file GZIPInputStream in = null; OutputStream out = null; try < in = new GZIPInputStream(new FileInputStream("file.txt.gz")); out = new FileOutputStream("file.txt"); byte[] buf = new byte[1024 * 4]; int len; while ((len = in.read(buf)) >0) < out.write(buf, 0, len); >> catch (IOException e) < e.printStackTrace(); >finally < if (in != null) try < in.close(); >catch (IOException ignore) < >if (out != null) try < out.close(); >catch (IOException ignore) < >>
@StanislavPalatnik probably cause of parts of the code that haven’t to do much with the problem 😉 (BTW I wasn’t a downvoter) catch (IOException e) < e.printStackTrace(); >finally < if (in != null) try < in.close(); >catch (IOException ignore) < >if (out != null) try < out.close(); >catch (IOException ignore) < >>
With Java 9 this can be simplified to try (InputStream in = new GZIPInputStream(new FileInputStream(«file.txt.gz»)); OutputStream out = new FileOutputStream(«file.txt»)) < in.transferTo(out); >
Note that TrueZip, the library suggested below, has been superseded by TrueVFS.
I’ve found the TrueZIP library useful. It allows you to treat archive files as if they’re just another file system and use the familiar Java I/O APIs.
Unlike the java.util.zip API, TrueZIP provides random access to the contents of the archive, so file size should not be a concern. If I remember correctly, it will detect archive files and not try to redundantly compress them when you put them into an archive.
The TrueZIP API provides drop-in replacements for the well-known classes File , FileInputStream and FileOutputStream . This design makes TrueZIP very simple to use: All that is required to archive-enable most client applications is to add a few import statements for the package de.schlichtherle.io and add some type casts where required.
Now you can simply address archive files like directories in a path name. For example, the path name «archive.zip/readme» addresses the archive entry readme within the ZIP file archive.zip . Note that file name suffixes are fully configurable and TrueZIP automatically detects false positives and reverts back to treat them like ordinary files or directories. This works recursively, so an archive file may even be enclosed in another archive file, like in outer.zip/inner.zip/readme .
Zipping files with java
Кроме общего функционала для работы с файлами Java предоставляет функциональность для работы с таким видом файлов как zip-архивы. Для этого в пакете java.util.zip определены два класса — ZipInputStream и ZipOutputStream
ZipOutputStream. Запись архивов
Для создания архива используется класс ZipOutputStream. Для создания объекта ZipOutputStream в его конструктор передается поток вывода:
ZipOutputStream(OutputStream out)
Для записи файлов в архив для каждого файла создается объект ZipEntry , в конструктор которого передается имя архивируемого файла. А чтобы добавить каждый объект ZipEntry в архив, применяется метод putNextEntry() .
import java.io.*; import java.util.zip.*; public class Program < public static void main(String[] args) < String filename = "notes.txt"; try(ZipOutputStream zout = new ZipOutputStream(new FileOutputStream("output.zip")); FileInputStream fis= new FileInputStream(filename);) < ZipEntry entry1=new ZipEntry("notes.txt"); zout.putNextEntry(entry1); // считываем содержимое файла в массив byte byte[] buffer = new byte[fis.available()]; fis.read(buffer); // добавляем содержимое к архиву zout.write(buffer); // закрываем текущую запись для новой записи zout.closeEntry(); >catch(Exception ex) < System.out.println(ex.getMessage()); >> >
После добавления объекта ZipEntry в поток нам также надо добавить в него и содержимое файла. Для этого используется метод write, записывающий в поток массив байтов: zout.write(buffer); . В конце надо закрыть ZipEntry с помощью метода closeEntry() . После этого можно добавлять в архив новые файлы — в этом случае все вышеописанные действия для каждого нового файла повторяются.
Чтение архивов. ZipInputStream
Для чтения архивов применяется класс ZipInputStream . В конструкторе он принимает поток, указывающий на zip-архив:
ZipInputStream(InputStream in)
Для считывания файлов из архива ZipInputStream использует метод getNextEntry() , который возвращает объект ZipEntry . Объект ZipEntry представляет отдельную запись в zip-архиве. Например, считаем какой-нибудь архив:
import java.io.*; import java.util.zip.*; public class Program < public static void main(String[] args) < try(ZipInputStream zin = new ZipInputStream(new FileInputStream("output.zip"))) < ZipEntry entry; String name; while((entry=zin.getNextEntry())!=null)< name = entry.getName(); // получим название файла System.out.printf("File name: %s \n", name); // распаковка FileOutputStream fout = new FileOutputStream("new" + name); for (int c = zin.read(); c != -1; c = zin.read()) < fout.write(c); >fout.flush(); zin.closeEntry(); fout.close(); > > catch(Exception ex) < System.out.println(ex.getMessage()); >> >
ZipInputStream в конструкторе получает ссылку на поток ввода. И затем в цикле выводятся все файлы и их размер в байтах, которые находятся в данном архиве.
Затем данные извлекаются из архива и сохраняются в новые файлы, которые находятся в той же папке и которые начинаются с «new».
Java Zip and Unzip Files and Folders
Here we will learn about how to zip and unzip files and folders in java.
If information contains redundant data it will be tough to store and transfer the data. So we will go for compression which will give efficient representation of the data. For zipping many algorithms are there.
Java provides the java.util.zip package for zipping and unzipping the files. The class ZipOutputStream will be useful for compressing. It is an output stream filter which will write files to any File output stream in zip format. The class ZipInputStream will be useful for decompressing. It is an input stream which will read files that are in zip format.
How to Zip Files in Java?
For zipping purpose we have many algorithms. If we are not mentioning any alogorithm through any method it will use default compression method that is DEFLATED compression. Deflated compression is almost near to Huffman coding.
Now we will see one example.
Problem: We want to zip the directory which has multiple files along with sub folders.
Solution: Zipping a file is very easy. But when we are zipping a folder which contains sub folders we should take care about the relative paths of files which have to be zipped.
For example take directory named as first which is located in E disk. It contains one subfolder along with multiple files. We have to zip it now.
Step 1: We should list out all files in the directory. listFiles() method will return total files (directories + files) in that directory. We can use this method to list out the total files. For this we have to check whether it is a directory or not. If it is a directory then again list out the files in that subdirectory. Continue this process until all files are listed out. This is recursive process.
Step 2: After listing out the files, we should create a file output stream for output zip folder. So that it will create output zip folder and write the contents to it.
Step 3: We should create a zip output stream which will write file contents to the file output stream in the format of zip.
- Create an input stream for a file. So that it can read file contents.
- Create a zip Entry object for file with relative path only because if use absolute path it can’t find files so zipping is not possible. So that we can add this file to zip out stream. For adding putNextEntry() method of ZipOutputStream will be useful.
- Now zip output stream will write these file contents in the format of zip to the file output stream.
- Close zip entry and close file input stream
This is the process of zipping one file. So do the same for every file in the list.
Step 5: As zipping of all files is done. Now close all IO streams. Proper closing of IO streams will prevent the corruption of zip files.
How to zip files and folders in Java?
In above code, we compress a file using java.util.zip package. But we have an issue. That is, if we select multiple files then only one file is being compressed. If we select a folder, the compression simply won’t work. How can I fix this to compress either a file, files, folder, folders, or even nested folders? Java zip package does support .zip, .tar, .tarGz and tarZ. So the solution should not be something which is limited to .zip extension as well.
@SercanOzdemir: His example differs, we have to identfy whether the selected is a file or folder as well..
3 Answers 3
Here is my solution that uses the new java.nio package. Just call zipDir giving it the path to the directory. It will create a zip file in the same location but called .zip .
private static Path buildPath(final Path root, final Path child) < if (root == null) < return child; >else < return Paths.get(root.toString(), child.toString()); >> private static void addZipDir(final ZipOutputStream out, final Path root, final Path dir) throws IOException < try (DirectoryStreamstream = Files.newDirectoryStream(dir)) < for (Path child : stream) < Path entry = buildPath(root, child.getFileName()); if (Files.isDirectory(child)) < addZipDir(out, entry, child); >else < out.putNextEntry(new ZipEntry(entry.toString())); Files.copy(child, out); out.closeEntry(); >> > > public static void zipDir(final Path path) throws IOException < if (!Files.isDirectory(path)) < throw new IllegalArgumentException("Path must be a directory."); >BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(path.toString() + ".zip")); try (ZipOutputStream out = new ZipOutputStream(bos)) < addZipDir(out, path.getFileName(), path); >>
The zip libraries for java cannot be used to compress folders in simpler way like — compress this folder.
You need to do the test if the input is folder or file by yourself. If it is a file — add it to the zip. If it is a folder — iterate the folder and add each file to the zip. For the subfolders to the same. To add more than one file to the Zip you need to create ZipEntry for each file.
You can try this code which works for me:
public static void zip(File directory, File zipfile) throws IOException < URI base = directory.toURI(); Dequequeue = new LinkedList(); queue.push(directory); OutputStream out = new FileOutputStream(zipfile); Closeable res = out; try < ZipOutputStream zout = new ZipOutputStream(out); res = zout; while (!queue.isEmpty()) < directory = queue.pop(); for (File kid : directory.listFiles()) < String name = base.relativize(kid.toURI()).getPath(); if (kid.isDirectory()) < queue.push(kid); name = name.endsWith("/") ? name : name + "/"; zout.putNextEntry(new ZipEntry(name)); >else < zout.putNextEntry(new ZipEntry(name)); copy(kid, zout); zout.closeEntry(); >> > > finally < res.close(); >>
Updated from this answer, which fixes issue with each file been added to it’s own directory. Also better supports Windows explorer.
import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class Test < public static void main(String agrs[]) < ZipUtils appZip = new ZipUtils(); appZip.zipIt(new File(source directory), new File(dest zip)); >public static class ZipUtils < private final ListfileList; private List paths; public ZipUtils() < fileList = new ArrayList<>(); paths = new ArrayList<>(25); > public void zipIt(File sourceFile, File zipFile) < if (sourceFile.isDirectory()) < byte[] buffer = new byte[1024]; FileOutputStream fos = null; ZipOutputStream zos = null; try < // This ensures that the zipped files are placed // into a folder, within the zip file // which is the same as the one been zipped String sourcePath = sourceFile.getParentFile().getPath(); generateFileList(sourceFile); fos = new FileOutputStream(zipFile); zos = new ZipOutputStream(fos); System.out.println("Output to Zip : " + zipFile); FileInputStream in = null; for (File file : this.fileList) < String path = file.getParent().trim(); path = path.substring(sourcePath.length()); if (path.startsWith(File.separator)) < path = path.substring(1); >if (path.length() > 0) < if (!paths.contains(path)) < paths.add(path); ZipEntry ze = new ZipEntry(path + "/"); zos.putNextEntry(ze); zos.closeEntry(); >path += "/"; > String entryName = path + file.getName(); System.out.println("File Added : " + entryName); ZipEntry ze = new ZipEntry(entryName); zos.putNextEntry(ze); try < in = new FileInputStream(file); int len; while ((len = in.read(buffer)) >0) < zos.write(buffer, 0, len); >> finally < in.close(); >> zos.closeEntry(); System.out.println("Folder successfully compressed"); > catch (IOException ex) < ex.printStackTrace(); >finally < try < zos.close(); >catch (IOException e) < e.printStackTrace(); >> > > protected void generateFileList(File node) < // add file only if (node.isFile()) < fileList.add(node); >if (node.isDirectory()) < File[] subNote = node.listFiles(); for (File filename : subNote) < generateFileList(filename); >> > > >
Zipping and Unzipping in Java
In this post, we will learn Zipping and Unzipping in Java using java.util.zip API. We will also discuss some third-party API to perform these tasks.
1. Zip a File
We will be performing a simple operation to zip a single file into an archive.
public class ZipSingleFile < public static void main(String[] args) throws IOException < String sourceFile = "/Users/umesh/personal/tutorials/source/index.html"; String zipName="/Users/umesh/personal/tutorials/source/testzip.zip"; File targetFile = new File(sourceFile); ZipOutputStream zipOutputStream = new ZipOutputStream(new FileOutputStream(zipName)); zipOutputStream.putNextEntry(new ZipEntry(targetFile.getName())); FileInputStream inputStream = new FileInputStream(targetFile); final byte[] buffer = new byte[1024]; int length; while((length = inputStream.read(buffer)) >= 0) < zipOutputStream.write(buffer, 0, length); >zipOutputStream.close(); inputStream.close(); > >
2. Zip Multiple Files / Directories Java8
Creating zip for a single file is not very interesting or real life solution, we will be improving our program with an option to zip entire directory using Files.walk method.
public class ZipDirectory < public static void main(String[] args) throws IOException < String sourceDirectoryPath = "/Users/umesh/personal/tutorials/source"; String zipFilePath = "/Users/umesh/personal/tutorials/source.zip"; zipDirectory(sourceDirectoryPath, zipFilePath); >public static void zipDirectory(String sourceDirectoryPath, String zipPath) throws IOException < Path zipFilePath = Files.createFile(Paths.get(zipPath)); try (ZipOutputStream zipOutputStream = new ZipOutputStream(Files.newOutputStream(zipFilePath))) < Path sourceDirPath = Paths.get(sourceDirectoryPath); Files.walk(sourceDirPath).filter(path ->!Files.isDirectory(path)) .forEach(path -> < ZipEntry zipEntry = new ZipEntry(sourceDirPath.relativize(path).toString()); try < zipOutputStream.putNextEntry(zipEntry); zipOutputStream.write(Files.readAllBytes(path)); zipOutputStream.closeEntry(); >catch (Exception e) < System.err.println(e); >>); > > >
2. Zip Multiple Files / Directories Java7
public class ZipDirectoryFilesWalk < public static void main(String[] args) throws IOException < Path sourceDirectoryPath = Paths.get("/Users/umesh/personal/tutorials/source"); Path zipFilePath = Paths.get("/Users/umesh/personal/tutorials/source.zip"); zipDirectory(sourceDirectoryPath, zipFilePath); >public static void zipDirectory(Path sourceDirectory, Path zipFilePath) throws IOException < try (FileOutputStream fileOutputStream = new FileOutputStream(zipFilePath.toFile()); ZipOutputStream zipOutputStream = new ZipOutputStream(fileOutputStream) ) < Files.walkFileTree(sourceDirectory, new SimpleFileVisitor() < @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException < zipOutputStream.putNextEntry(new ZipEntry(sourceDirectory.relativize(file).toString())); Files.copy(file, zipOutputStream); zipOutputStream.closeEntry(); return FileVisitResult.CONTINUE; >@Override public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException < zipOutputStream.putNextEntry(new ZipEntry(sourceDirectory.relativize(dir).toString() + "/")); zipOutputStream.closeEntry(); return FileVisitResult.CONTINUE; >>); > > >
In the above examples, we saw different options to Zipping Files and Directories in Java using Java7 and Java8.
3. Unzip Directory
public class UnZipDirectory < public static void main(String[] args) throws IOException < String unzipLocation = "/Users/umesh/personal/tutorials/unzip"; String zipFilePath = "/Users/umesh/personal/tutorials/source.zip"; unzip(zipFilePath, unzipLocation); >public static void unzip(final String zipFilePath, final String unzipLocation) throws IOException < if (!(Files.exists(Paths.get(unzipLocation)))) < Files.createDirectories(Paths.get(unzipLocation)); >try (ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipFilePath))) < ZipEntry entry = zipInputStream.getNextEntry(); while (entry != null) < Path filePath = Paths.get(unzipLocation, entry.getName()); if (!entry.isDirectory()) < unzipFiles(zipInputStream, filePath); >else < Files.createDirectories(filePath); >zipInputStream.closeEntry(); entry = zipInputStream.getNextEntry(); > > > public static void unzipFiles(final ZipInputStream zipInputStream, final Path unzipFilePath) throws IOException < try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(unzipFilePath.toAbsolutePath().toString()))) < byte[] bytesIn = new byte[1024]; int read = 0; while ((read = zipInputStream.read(bytesIn)) != -1) < bos.write(bytesIn, 0, read); >> > >
In this post, we learned how to zip and unzip files or directory using core Java features. Read our articles List all files from a directory in Java to learn how to recursively transverse folders to zip multiple files.
If you want more sophisticated API to perform other operation on the zip files, you can check following open source libraries
All the code of this article is available Over on Github. This is a Maven-based project.