in memory file zip in java - 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.

Related

Java.util.zip.ZipException: invalid entry size (expected 18401 but got 0 bytes) when using putNextEntry() in ZipOutputStream?

I converted a zip file of 5 files into a byte array. I want to output a zip file on my disk from that bytearray. My process was first reading the byte[] into a ByteArrayInputStream then into a ZipInputStream.
InputStream plainTextStream = new ByteArrayInputStream(plainText);
ZipInputStream zipInStream = new ZipInputStream(plainTextStream);
I want this to be outputted into a zip file on my disk so here I thought I will need a file and a ZipOutPutStream passing that zip file.
ZipOutputStream zipOutStream = new ZipOutputStream(new FileOutputStream(file));
With a zip entry I traversed the ZipInPutStream writing to a FileOutputStream each entry, using a buffer. At the end of each main loop I put an entry into the ZipOutPutStream.
ZipEntry entry = null;
while((entry = zipInStream.getNextEntry()) != null){
FileOutputStream fileOutStream = new FileOutputStream(entry.getName());
byte[] byteBuff = new byte[1024];
int bytesRead = 0;
while ((bytesRead = zipInStream.read(byteBuff)) != -1)
{
fileOutStream.write(byteBuff, 0, bytesRead);
}
fileOutStream.close();
zipOutStream.putNextEntry(entry);
zipInStream.closeEntry();
}
I add the first file from the zip (there are 5 files), but when trying to add the 2nd file, I get an error on
zipOutStream.putNextEntry(entry)
java.util.zip.ZipException: invalid entry size (expected 18401 but got 0 bytes)
Through debugging I can't figure out where it goes wrong. I assume it may have something to do with the buffer when putting in the first outputstream(entry.getName())? The bytesRead while loop could be an issue.
This is all assuming the logic makes sense. I hope I can approach a solution to this error.
You never write the content of the zipped files to the zip output stream.
You don't need to write the output to a file stream, just write it directly to the zip output stream.
You should be using try-with-resources.
try (ZipInputStream zipInStream = new ZipInputStream(new ByteArrayInputStream(plainText));
ZipOutputStream zipOutStream = new ZipOutputStream(new FileOutputStream(file));
) {
byte[] byteBuff = new byte[1024];
for (ZipEntry entry; (entry = zipInStream.getNextEntry()) != null; ) {
zipOutStream.putNextEntry(entry);
for (int bytesRead; (bytesRead = zipInStream.read(byteBuff)) != -1; ) {
zipOutStream.write(byteBuff, 0, bytesRead);
}
}
}
There is no need to call closeEntry().
To resolve (expected 18401 but got 0 bytes)
Create a new blank excel file.
Copy data from the file you copied from zip to the new created file in step 1.
Use the new file, it should work as its worked for me.
Thanks.

Corrupted zip file using ZipOutputStream

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());
}
}

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

Convert ZipOutputStream to FileInputStream

i have a code that takes a file, zip it, and stores it in a database
looks like this
// STEP 1 - Create a ZIP file
byte[] buffer = new byte[1024];// 18024
ZipOutputStream outZip = new ZipOutputStream(new FileOutputStream("file.zip"));
outZip.setLevel(Deflater.DEFAULT_COMPRESSION);
FileInputStream in = new FileInputStream(c:\file.hex);
outZip.putNextEntry(new ZipEntry("file.hex"));
int len;
while (( len = in.read(buffer)) > 0)
outZip.write(buffer, 0, len);
outZip.closeEntry();
in.close();
outZip.close();
// STEP 2 - Insert zip file to DB
file = new File("file.zip");
FileInputStream fis = new FileInputStream( file );
the fis object i store in the DB
but i would like to avoid the filesystem completely , and not create the file.zip.
i think i need to convert the ZipOutputStream into FileInputStream directly
but i could not find a way to do it.
is there an easy way to accomplish this ?
i also have the same problem when i extract the file, in order to read it i must create 2 different temporary files - file.zip and file.hex
You just do not have to create FileOutputStream at all. Use ByteArrayOutputStream instead:
ByteArrayOutputStream zipBytes = new ByteArrayOutputStream()
ZipOutputStream outZip = new ZipOutputStream(zipBytes);
// run your code that adds zip entries....
zipBytes.getBytes() // returns array of bytes that contain your zipped information.

Categories