thwomp68
thwomp68

Reputation: 145

Java - ZipOutputStream within try-with-resources

I have this Java code that creates and saves a zip file with a text file inside it:

public static void main(String[] args) {
    try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
         ZipOutputStream zos = new ZipOutputStream(baos)) {
        String s = "Hello, world!";
        ZipEntry entry = new ZipEntry("text_file.txt");
        zos.putNextEntry(entry);
        zos.write(s.getBytes());
        zos.closeEntry();

        try (OutputStream os = new FileOutputStream("zip_file.zip")) {
            baos.writeTo(os);
            System.out.println("ZIP file created successfully");
        }
    } catch (IOException e) {
        System.out.println("Error: " + e);
    }
}

The problem with this code is that it creates a zip file that is corrupted, so I cannot extract or even open the content within it.

Eventually I found out that if I manually close the ZipOutputStream by putting zos.close() after zos.closeEntry() the zip file is created successfully without corruption. Honestly this does not make any sense to me since I have declared the ZipOutputStream inside a try-with-resource statement, so I would have expected it to close automatically.

So it seems that the try-with-resource statement does not actually close the stream. Am I doing something wrong?

Any help would be appreciated.

OS: Windows 10

Note: I used ByteArrayOutputStream because in the real scenario I have to create the zip file in memory, since I don't have a folder on a disk to rely on.

Upvotes: 2

Views: 2504

Answers (3)

Lin Xuhao
Lin Xuhao

Reputation: 29

To keep everything in same Try-catch, another approach is to finish & flush the ZipOutputStream before using the content of ByteArrayOutputStream inside it (which can be incomplete, this is why it may generate corrupted zip), for example :

 try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
     ZipOutputStream zos = new ZipOutputStream(baos)) {
    //codes insert into zip
    zos.finish(); //finish writing
    zos.flush(); //flush any buffer inside
    //use baos 
 } catch (IOException e) {
    System.out.println("Error: " + e);
 }

Upvotes: 0

thwomp68
thwomp68

Reputation: 145

It turned out that I was misplacing the ByteArrayOutputStream object: I should have not closed it by placing it inside the try-with-resource statement because, as explained here, ByteArrayOutputStream is purely memory based so the allocated memory will be released automatically by the garbage collector.

The following code produces the desired result:

public static void main(String[] args) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();

    try (ZipOutputStream zos = new ZipOutputStream(baos)) {
        String s = "Hello, world!";
        ZipEntry entry = new ZipEntry("text_file.txt");
        zos.putNextEntry(entry);
        zos.write(s.getBytes());
        zos.closeEntry();
    } catch (IOException e) {
        System.out.println("Error: " + e);
    }

    try (OutputStream os = new FileOutputStream("zip_file.zip")) {
        baos.writeTo(os);
        System.out.println("ZIP file created successfully");
    } catch (IOException e) {
        System.out.println("Error: " + e);
    }
}

Upvotes: 0

erickson
erickson

Reputation: 269697

The try-with-resources does close the ZipOutputStream. It's just that it closes the zip stream after you copy its content to the FileOutputStream. How do you expect work that happens after the file has been closed to affect the content of the file?

I recommend replacing the ByteArrayOutputStream with the FileOutputStream. What need do you have to buffer?

try (OutputStream os = new FileOutputStream("zip_file.zip");
     ZipOutputStream zos = new ZipOutputStream(os)) {
  String s = "Hello, world!";
  ZipEntry entry = new ZipEntry("text_file.txt");
  zos.putNextEntry(entry);
  zos.write(s.getBytes());
  zos.closeEntry();
}
System.out.println("ZIP file created successfully");

Upvotes: 2

Related Questions