Corrupted zip file using ZipOutputStream - java

I'm trying to create a zip file to be able to send multiple files over http.
My issue is that the Zip file that is generated is "corrupted" before and after the file has been send. The issue is i'm not able to find what i did wrong as i'm getting no errors inside the console.
So does someone has an idea file my generated zip file is corrupted ?
This is my code :
OutputStream responseBody = t.getResponseBody();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
int counter = 1;
for (PDDocument doc : documents)
{
ZipEntry zipEntry = new ZipEntry("document" + counter);
zos.putNextEntry(zipEntry);
ByteArrayOutputStream docOs = new ByteArrayOutputStream();
doc.save(docOs);
docOs.close();
zos.write(docOs.toByteArray());
zos.closeEntry();
zos.finish();
zos.flush();
counter++;
}
zos.close();
baos.close();
responseBody.write(baos.toByteArray());
responseBody.flush();
Thank you for your help !

You need to remove zos.finish() from inside the loop as it terminates the ZIP entries, as it is handled by zos.close() at end of the stream.
With very large streams you will be better off sending ZIP directly to responseBody bypassing ByteArrayOutputStream memory buffer.
If you are still having problems check the content type of the output is set. It might be easier to debug by temporarily writing the byte[] to file to check the ZIP format you are sending with:
Files.write(Path.of("temp.zip"), baos.toByteArray());
This outline below shows sending a simple ZIP over http (from a servlet, adjust the first 2 lines to appropriate calls for "t"). This may help you check which step of your code causes the corruption if you work back to adding your own document objects inside the loop:
// MUST set response content type:
// resp.setContentType("application/zip");
OutputStream out = resp.getOutputStream(); // or t.getResponseBody();
try(ZipOutputStream zos = new ZipOutputStream(out))
{
while (counter-- > 0)
{
ZipEntry zipEntry = new ZipEntry("document" + counter+".txt");
zos.putNextEntry(zipEntry);
zos.write(("This is ZipEntry: "+zipEntry.getName()+"\r\n").getBytes());
}
}

Related

How to download the folder from minio as zip file?

The steps I followed are.
get all objects from recursive objects
Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder()
.bucket(bucketName).recursive(true).build());
Then getting all streams of matching the prefix
InputStream stream = minioClient.getObject(GetObjectArgs.builder()
.bucket(bucketName).object(objectName).build());
the list of multiple stream got by the InputStream stream How do we convert it into zip file ?
tried the following code but it's (zipOut) coming as null.
downloading empty zip, How do we fix this ?
ByteArrayOutputStream fos = new ByteArrayOutputStream();
ZipOutputStream zipOut = new ZipOutputStream(fos);
ZipEntry zipEntry1 = new ZipEntry(objectName);
zipEntry1.setSize(resource.contentLength());
zipEntry1.setTime(System.currentTimeMillis());
zipOut.putNextEntry(zipEntry1);
StreamUtils.copy(stream.readAllBytes(), zipOut);
zipOut.closeEntry();
Thanks in advance.

Java Zip File Created With ByteArrayOutputStream is smaller than with FileOutputStream

The Zip file output from using ByteArrayOuputStream is smaller than that with FileOutputStream.
In Java I am creating a Zip file. Which opens successfully when I use FileOutputStream.
When switching the FileOutputStream to a ByteArrayOutputStream I get a slightly smaller file, about 221 bytes. And this file won't open, but has the same contents as the other but missing 221 bytes.
The logic I use to construct the Zip file is the same in both cases. Therefore I will share the essential logic without the zip creation (let me know if you need this).
ByteArrayOutputStream baos = new ByteArrayOutputStream();
zipOS = new ZipOutputStream(baos);
Then I create the Zip file, and then ...
zipOS.flush();
baos.flush();
baos.writeTo(new FileOutputStream("TEST_AS_BYTESTREAM_1.zip"));
zipOS.close();
baos.close();
I'm using the baos.writeTo() to bypass this to prove it's not the HTTP response but the ByteArrayOutputStream element thats an issue.
When using baos.writeTo(new FileOutputStream("TEST_AS_BYTESTREAM_1.zip"));
I get a smaller file that doesn't open as a Zip.
This logic is in a java controller so that a link clicked on web page will down load the zip file without touching any file systems excepted the users.
Here is the code using FileOutputStream which works...
FileOutputStream baos = new FileOutputStream("TEST_AS_FILE.zip");
zipOS = new ZipOutputStream(baos);
Aside from the the second code snippet isn't relevant as I would need to send the file created on the web server. But the point is, that Fileoutput stream with the same logic and ByteArrayOutputStream has a difference.
The following statement is wrong:
baos.writeTo(new FileOutputStream("TEST_AS_BYTESTREAM_1.zip"));
The javadoc of writeTo(OutputStream out) does not say anything about closing the OutputStream, which means it doesn't, so the last of the data is still sitting buffered in the un-closed, un-flushed FileOutputStream.
The correct way is:
try (OutputStream out = new FileOutputStream("TEST_AS_BYTESTREAM_1.zip")) {
baos.writeTo(out);
}
Also, a ByteArrayOutputStream does not need to be closed or flushed. As the javadoc of close() says it:
Closing a ByteArrayOutputStream has no effect.
You will however need to call finish() on an ZipOutputStream to complete the content. Since closing the zip stream automatically finishes it for you, the correct way to do this is:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (ZipOutputStream zipOS = new ZipOutputStream(baos)) {
// create the Zip file content here
}
try (OutputStream out = new FileOutputStream("TEST_AS_BYTESTREAM_1.zip")) {
baos.writeTo(out);
}
When writing directly to a file:
try (ZipOutputStream zipOS = new ZipOutputStream(new FileOutputStream("TEST_AS_FILE.zip"))) {
// create the Zip file content here
}
Consider adding a BufferedOutputStream for better performance.
The final answer is as follows. Many thanks to the answer previously given.
Basically I needed to use the .finish() method of the ZipOuputStream. Everything else remained the same.
Pre zip creation code same as before
ByteArrayOutputStream baos = new ByteArrayOutputStream();
zipOS = new ZipOutputStream(baos);
Post zip creation...
zipOS.flush();
zipOS.finish();
baos.flush();
documentBody = baos.toByteArray();
zipOS.close();
baos.close();
Then my HTTP response is
HttpHeaders headers = new HttpHeaders();
headers.setContentType(new MediaType("application", "zip"));
headers.setContentLength(documentBody.length);
headers.add("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
ByteArrayResource resource = new ByteArrayResource(documentBody);
return ResponseEntity.ok()
.headers(headers)
.body(resource);
This way I avoid the creation of a file which is nice.
Many thanks for the assistance.

Java: ZipOutputStream to compress chunks of data to single zip file

Follow up of Question: Java: how to compress a byte[] using ZipOutputStream without intermediate file
I can zip data without an intermediate file (or memory file). I now need to zip chunks of data and add them to a single zip file.
I am using a single ZipOutputStream as suggested in the previous question.
String infile = "test.txt";
FileInputStream in = new FileInputStream(infile);
String outfile = "test.txt.zip";
FileOutputStream out = new FileOutputStream(outfile);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
ZipEntry entry = new ZipEntry("test_unzip.txt");
entry.setSize(2048);
zos.putNextEntry(entry);
int len = 0;
while (len > -1) {
byte[] buf = new byte[10];
len = in.read(buf);
zos.write(buf);
out.write(baos.toByteArray());
baos.reset();
}
zos.closeEntry();
zos.finish();
zos.close();
in.close();
out.close();
I have tried different sizes for buf, reordering zos.finish and zos.closeEntry, and also tried with and without baos.reset.
I have also tried reading the entire contents of infile into a single buf but still not working.
I expected a valid .zip file that will unzip into test_unzip.txt.
However, when i try unzip test.txt.zip on my command line i get the following error:
End-of-central-directory signature not found. Either this file is not
a zipfile, or it constitutes one disk of a multi-part archive. In the latter case the central directory and zipfile comment will be found on the last disk(s) of this archive.
unzip: cannot find zipfile directory in one of test.txt.zip or
test.txt.zip.zip, and cannot find test.txt.zip.ZIP, period.

JAVA char[] to Zip to File

I have a chararray which holds coordinates in every index. I want to compress the array with zip algorithm and write the zipped version of the array to a file. My idea was that i run the chararray through a zip stream and write it afterwards directly to a file like test.txt. The problem is, that nothing is written to the file after the execution of the code. Can somebody please help me solve that problem?
Kind regards Lorenzo
Here my current code:
byte[] bytes = Charset.forName("UTF-8").encode(CharBuffer.wrap(sample)).array();
FileOutputStream out = new FileOutputStream(cscFile, true);
byte[] compressed = new byte[bytes.length];
ByteArrayInputStream bi = new ByteArrayInputStream(bytes);
ZipInputStream zi = new ZipInputStream(bi);
ZipEntry entry = null;
while ((entry = zi.getNextEntry()) != null) {
zi.read(compressed);
for(int i = 0; i<bytes.length;i++){
out.write(compressed[i]);
}
out.flush();
out.close();
zi.closeEntry();
}
zi.close();
You will probably have to slightly modify your code with something like this:
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File("your zip file name")));
ZipEntry entry = new ZipEntry("zipped file name");
entry.setSize(bytes.length);
zos.putNextEntry(entry);
zos.write(bytes);
zos.closeEntry();
zos.close();
Explanation:
To compress writen data, use ZipOutputStream and give created FileOutputStream as constructor argument. After that, create ZipEntry, which represents file inside your zip file and write contents of byte array into it.
Hope it helps.
See similar question here: https://stackoverflow.com/a/357892/3115098

in memory file zip in java

I am trying to read multiple files (can be of any format i.e. pdf, txt, tiff etc) from URLs and zipping them using ZipOutputStream. My code looks like this:
// using in-memory file read
// then zipping all these files in-memory
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
.....
URL url = new URL(downloadUrl); // can be multiple URLs
ByteArrayOutputStream bais = new ByteArrayOutputStream();
InputStream is = url.openStream();
byte[] byteChunk = new byte[4096];
int n;
while ( (n = is.read(byteChunk)) > 0 )
{
bais.write(byteChunk, 0, n);
}
byte[] fileBytes = bais.toByteArray();
ZipEntry entry = new ZipEntry(fileName);
entry.setSize(fileBytes.length);
zos.putNextEntry(entry);
zos.write(fileBytes);
zos.closeEntry();
// close the url input stream
is.close();
// close the zip output stream
zos.close();
// read the byte array from ByteArrayOutputStream
byte[] zipFileBytes = baos.toByteArray();
String fileContent = new String(zipFileBytes);
I am then passing this content "fileContent" to my perl frontend application.
And I m using perl code to download this zipped file:
WPHTTPResponse::setHeader( 'Content-disposition', 'attachment; filename="test.zip"' );
WPHTTPResponse::setHeader( 'Content-type', 'application/zip');
print $result; // string coming from java application
But the zip file it is giving is corrupted. I think something in going wrong with the data translation.
I'd appreciate any help.
Your problem is thinking that you can output your zip bytes into a string. This string can not be used to reproduce the zip content again. You need to either use the raw bytes or encode the bytes into something that can be represented as a string, such as base64 encoding.

Categories