javazipfileoutputstreamzipoutputstream

Generated zip file is always corrupted after download on Mac


Lets say that I have a collection of objects which I would like to wrap with zip. My zip conversion class looks like:

public class ZipFile {

    public byte[] createZipByteArray(String fileName, byte[] content) throws IOException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        try (byteArrayOutputStream; ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) {
            zipOutputStream.setLevel(Deflater.NO_COMPRESSION);
            ZipEntry zipEntry = new ZipEntry(fileName);
            zipOutputStream.putNextEntry(zipEntry);
            zipOutputStream.write(content);
            zipOutputStream.closeEntry();
            zipOutputStream.flush();
        }
        return byteArrayOutputStream.toByteArray();
    }

}

My controller looks like:

    @GetMapping(value = "objectWithDetails/zip", produces = "application/zip")
    @RolesAllowed("QUERY_DATA")
    public ResponseEntity<byte[]> getObjectWithDetails(String limit, HttpServletResponse response) throws IOException {
        response.setContentType("application/zip");

        List<ObjectSpecification> objectRepresentation = recipeService.getObjectRepresentation(limit);

        byte[] objectsBytes = objectMapper.writeValueAsBytes(objectRepresentation);

        ZipFile zipFile = new ZipFile();

        return ResponseEntity
                .ok()
                .contentType(MediaType.valueOf("application/zip"))
                .header("Content-Encoding", "UTF-8")
                .header("Content-Disposition", String.format("attachment; filename=\"details_%s.zip\"", dateTimeProvider.nowDate()))
                .body(zipFile.createZipByteArray(String.format("details_%s.zip", dateTimeProvider.nowDate()), objectsBytes));

    }

In swagger, I have a download option for the generated zip files. After all, when I try to open the downloaded file I receive the following:

Unable to expand filename.zip. It is in an unsupported format.

I tried closing ZipOutputStream and ByteArrayOutputStream in a different configuration. Applied a different option for the content type in the controller but still, I cannot find out why all zip files are corrupted. I will be grateful for suggestions on what I miss. Cheers!


Solution

  • I managed to find out the root cause with @k314159 , namely, I had a zip file wrapped with a zip file. New version which works as expected.

        public static byte[] createZipByteArray(String fileName, byte[] content) throws IOException {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            try (byteArrayOutputStream; ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream)) {
                zipOutputStream.setLevel(Deflater.DEFLATED);
                ZipEntry zipEntry = new ZipEntry(fileName);
                zipOutputStream.putNextEntry(zipEntry);
                zipOutputStream.write(content);
                zipOutputStream.closeEntry();
                zipOutputStream.flush();
            }
            return byteArrayOutputStream.toByteArray();
        }
    

    and controller

        @GetMapping(value = "objectWithDetails", produces = "application/zip")
        @RolesAllowed("QUERY_DATA")
        public ResponseEntity<byte[]> getObjectWithDetails(String limit, HttpServletResponse response) throws IOException {
            response.setContentType("application/zip");
            List<ObjectSpecification > objectRepresentation = objectService.getObjectRepresentation(limit);
    
            byte[] objectsBytes = objectMapper.writeValueAsBytes(objectRepresentation);
    
            return ResponseEntity
                    .ok()
                    .contentType(MediaType.valueOf("application/zip"))
                    .header("Content-Encoding", "UTF-8")
                    .header("Content-Disposition", getObjectFileName("attachment; filename=objects_"))
                    .body(createZipByteArray(getObjectFileName("objects_"), objectsBytes));
        }