4

I'm trying to zip files from server into a folder using ZipOutputStream. After archive download it can't be opened after double click. Error "The compressed (zipped) folder is invalid" occures. But if I open it from context menu - > 7zip -> open file it works normal. What can be reason of the problem?

sourceFileName="./file.txt"'
                    sourceFile = new File(sourceFileName);

                    try {
                        // set the content type and the filename
                       responce.setContentType("application/zip");
                        response.addHeader("Content-Disposition", "attachment; filename=" + sourceFileName + ".zip");
                        responce.setContentLength((int) sourceFile.length());


                        // get a ZipOutputStream, so we can zip our files together
                        ZipOutputStream outZip = new ZipOutputStream((responce.getOutputStream());

                        // Add ZIP entry to output stream.
                        outZip.putNextEntry(new ZipEntry(sourceFile.getName()));

                        int length = 0;
                        byte[] bbuf = new byte[(int) sourceFile.length()];

                        DataInputStream in = new DataInputStream(new FileInputStream(sourceFile));
                        while ((in != null) && ((length = in.read(bbuf)) != -1)) {
                            outZip.write(bbuf, 0, length);
                        }

                        outZip.closeEntry();
                        in.close();
                        outZip.flush();
                        outZip.close();

2 Answers2

4

7Zip can open a wide variety of zip formats, and is relatively tolerant of oddities. Windows double-click requires a relatively specific format and is far less tolerant.

You need to look up the zip format and then look at your file (and "good" ones) with a hex editor (such as Hex Editor Neo), to see what may be wrong.

(One possibility is that you're using the wrong compression algorithm. And there are several other variations to consider as well, particularly whether or not you generate a "directory".)

Hot Licks
  • 47,103
  • 17
  • 93
  • 151
3

It could be that a close is missing. It could be that the path encoding in the zip cannot be handled by Windows. It might be that Windows has difficulty with the directory structure, or that a path name contains a (back)slash. So it is detective work, trying different files. If you immediately stream the zip to the HTTP response, then finish has to be called i.o. close.


After the code being posted:

The problem is the setContentLength giving the original file size. But when given, it should give the compressed size.

DataInputStream is not needed, and one should here do a readFully.

    responce.setContentType("application/zip");
    response.addHeader("Content-Disposition", "attachment; filename=file.zip");

     //Path sourcePath = sourceFile.toPath();
     Path sourcePath = Paths.get(sourceFileName);

     ZipOutputStream outZip = new ZipOutputStream((responce.getOutputStream(),
            StandardCharsets.UTF-8);

     outZip.putNextEntry(new ZipEntry(sourcePath.getFileName().toString()));
     Files.copy(sourcePath, outZip);
     outZip.closeEntry();

Either finish or closethe zip at the end.

     outZip.finish();
     //outZip.close();

     in.close();

I am not sure (about the best code style) whether to close the response output stream already oneself. But when not closing finish() must be called, flush() will not suffice, as at the end data is written to the zip.

For file names with for instance Cyrillic letters, it would be best to add a Unicode charset like UTF-8. In fact let UTF-8 be the Esperanto standard world-wide.

A last note: if only one file one could use GZipOutputstream for file.txt.gz or query the browser's capabilities (request parameters) and deliver it compressed as file.txt.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • @Ксения Шапошникова Welcome; I am curious – Joop Eggen Dec 19 '11 at 19:04
  • fileName in ContentDisposition header should just be "file.zip" without path. Content-Length should not be given, as you do not know (is compressed). You should replace outZip.close by outZip.finish; closing does the JEE server. – Joop Eggen Dec 19 '11 at 19:07
  • For one file, you could use GZipOutputStream, creating file.txt.gz. – Joop Eggen Dec 19 '11 at 19:09
  • DataInputSteam is for special cases int/long and other structured data. Maybe a BufferedInputStream would do. in != null can be removed. – Joop Eggen Dec 19 '11 at 19:14
  • I've followed all recommendations except usage of GZipOutputStream. But it doesn't works yet. If I try to paste the same code into a project which just takes file from local machine,packs it and saves on local machine too (in this case server is on the same machine as client) everything works. Maybe there are some pecularities when I work with remote server? – Ксения Шапошникова Dec 20 '11 at 11:33
  • Recapting: 7Zip gives the entire file, but Windows cannot open it. File names are not Cyrillic (e, x). Paths in 7Zip seem okay. Try as content type application/octet-stream, add one directory xxx/file.txt, but that cannot be it. I did a outZip.setLevel(9); in my code. Send your code to me joop_eggen at ya hoo dot de. It must be something simple. – Joop Eggen Dec 20 '11 at 13:43
  • can you write you email exactly, please? what do you mean to send to you? – Ксения Шапошникова Dec 20 '11 at 14:05
  • I am getting curious to the error; you can send the current piece of code to joop_eggen `at` yahoo `period` de. – Joop Eggen Dec 20 '11 at 14:13
  • Code has been sent. Thank you – Ксения Шапошникова Dec 20 '11 at 14:45
  • @КсенияШапошникова a bit very late, I found the error (setContentLength) and slightly improved the code. – Joop Eggen Oct 09 '18 at 09:00