How can I transform an uncompressed file into zipped bytes? - java

In Java for a JUnit test, I am trying to mock a function that downloads a Zip File from another external API's endpoint. To simulate the download, I need to zip a test file and transform it into bytes to use as the mock's return value. I do not need to write the zipped file back to the file system but use the bytes raw as they are.
mock(zipReturner.getZipBytes()).thenReturn(testFileAsZippedBytes("testFile.txt"))
private Optional<byte[]> testFileAsZippedBytes(String testFile) {
???
}

Sharing my answer, because all the other examples I found are much heavier, require many more lines of code looping over bytes, or require using external libraries to do the same thing.
To do this without the above, use a combination of ByteArrayOutputStream, as it has the toByteArray function, ZipOutputStream to write zipped bytes to the ByteArrayOutputStream and FileInputStream to read the test file from the file system.
private Optional<byte[]> testFileAsZippedBytes(String filePath, String fileName) throws IOException {
try (
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ZipOutputStream zipOutputStream = new ZipOutputStream(byteArrayOutputStream);
FileInputStream fileInputStream = new FileInputStream(filePath + fileName);
) {
ZipEntry zipEntry = new ZipEntry(fileName);
zipOutputStream.putNextEntry(zipEntry);
zipOutputStream.write(fileInputStream.readAllBytes());
zipOutputStream.finish();
return Optional.of(byteArrayOutputStream.toByteArray());
}
}
Use ZipEntry to add the file as an entry to the ZipOutputStream and write the bytes to the zip. Use zipOutputStream.finish() to ensure all contents are written to the stream and are ready to be consumed in the ByteArrayOutputStream, otherwise it was my experience that you would only get partial data when you call byteArrayOutputStream.toByteArray().

Related

ByteArrayOutputStream with zipStream to InputStream

Currently I want to create a zip file and make it available in the browser via HTTP response. Because I don't want to cache files on the hard disk I have to use streams all the time. The problem is that after creating the zip file I only have one ByteArrayOutputStream, but for sending the response I need an InputStream.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zip = new ZipOutputStream(baos);
// filling zip with multiple ZipEntrys
IOUtils.closeQuietly(zip);
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
IOUtils.closeQuietly(baos);
The line
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
gives me a null pointer every time.
What is wrong? Is there a stream not completely written? Additionally I have to do everything with Java 6 and Apache commons.

Creating ZIP file in memory

I need to create a ZIP file which consists of files that are created on-the-fly and have no persistence on the file system.
For example: I want to create an SQLite database in memory and after populating it with data I want to add it to a - not yet existing - ZIP file and than I want to actually write this ZIP file to the file system.
I found several approaches where the files, which are going to be the content of the archive, have to be read from the file system.
Is there actually a way to archive what I want to do? I hoped that compress-commons would help me but apparently they don't.
Do I miss something?
If the in memory object you are trying to zip is serializable, then this is quite easy.
You can take any serializable instance and turn it in to a byte[]. I have a utility method to do this:
public static byte[] convertToBytes(Object object) throws IOException {
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutput out = new ObjectOutputStream(bos)) {
out.writeObject(object);
out.flush();
return bos.toByteArray();
}
}
Once you have a that object represented in bytes, you can use a ZipOutputStream to zip it up:
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
GZIPOutputStream out = new GZIPOutputStream(bos); ) {
out.write(bytes);
out.finish();
byte[] compressed = bos.toByteArray(); // this is my compressed data
}
(I use Gzip here for simplicity but you can also create a zip with multiple entries, for example).

Zipping a file and sending it without saving the zipped file in the process

I want to open a specific file and zip it and send the byte array of the zipped result over UDP.
Now I checked the java zip API but it only give me to save the zipped file to the computer and opening it again and send it would be inefficiency.
Is there any way to do what I need without writing an implementation of the compression algorithm of my own ?
You cand wrap your ZipOutputStream around a ByteArrayOutputStream and get the bytes out of the ByteArrayOutputStream.
Something like this:
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ZipOutputStream zos = new ZipOutputStream(baos);
//write the entries
zos.close();
byte[] bytes = baos.toByteArray();

write a XSSFWorkbook to a zip file

I now have this problem. I want to write a excel file hold in this XSSFWorkbook (workbook) obj into a zip file eg(example.zip while contain this example.xlsx file) to a remote server.
I have tried following but not working, it created a folder with some odd files in the zip file
XSSFWorkbook workbook = new XSSFWorkbook();
//add some data
Zipoutputstream zipstream=new Zipoutputstream(//destination outputstream);
workbook.write(zipstream);
So do anyone knows what's the right way to do this? Thanks in advance
ps workbook.write(fileoutputstream) works but it only write to local disk as a flat file eg test.xlsx instead of inside a zip as I need.
Passing a a ZipOutputStream to XSSFWorkbook.write will result in the stream being hijacked and closed by the workbook. This is because an XSSFWorkbook writes a .xlsx which is itself a zip archive of xml and other files (you can unzip any .xslx to see what's in there).
If you're able to fit the excel file in memory, I've found this to work well:
ZipOutputStream zos = new ZipOutputStream(//destination outputstream);
zos.putNextEntry(new ZipEntry("AnExcelFile.xlsx"));
ByteArrayOutputStream bos = new ByteArrayOutputStream();
workbook.write(bos);
bos.writeTo(zos);
zos.closeEntry();
// Add other entries as needed
zos.close();
Calling close on ByteArrayOutputStream has no effect and can still be written to zos.
You are missing some necessary calls on your ZipOutputStream. You will need to create a ZipEntry for your spreadsheet file, then write it out. You'll need something like
zipstream.putNextEntry(new ZipEntry("example.xlsx"));
Then you should be able to call
workbook.write(zipstream);
But after that you'll need to close the entry before closing the stream.
zipstream.closeEntry();
Please see "Write And Read .Zip File From Java" for details on how to use Java's ZipOutputStream.
Also, be aware that .xlsx files are already compressed zip files, so placing it in a .zip file may not compress it very much.
A colleague of mine, M. Bunshaft, suggested a solution similar to that of Klugscheißer but that does not require the use of a ByteArrayOutputStream, and hence can accommodate larger output.
The idea is to subclass ZipOutputStream, overriding the close() method so it will not do a close.
public class UncloseableZipOutputStream extends ZipOutputStream
{
OutputStream os;
public UncloseableZipOutputStream( OutputStream os )
{
super(os);
}
#Override
/** just flush but do not close */
public void close() throws IOException
{
flush();
}
public void reallyClose() throws IOException
{
super.close();
}
}
Then, simply use it the way you would use the ZipOutputStream.
UncloseableZipOutputStream zos = new UncloseableZipOutputStream(//destination outputstream);
zos.putNextEntry(new ZipEntry("AnExcelFile.xlsx"));
workbook.write(zos);
zos.closeEntry(); // now this will not cause a close of the stream
// Add other entries as needed
zos.reallyClose();

Is there a Java zip library that can fix files, à la zip -FF?

I occasionally receive .zip files in my app that throw start of central directory not found;
zipfile corrupt. exceptions. These zip files open just fine in my Mac's Finder.
I can fix these files every time from the command line, using zip -FF bad.zip --out good.zip
Can any Java ZIP libraries out there accomplish the same thing?
You probably want to just let Java execute this command, because in strict terms zip is more like a container and it can contain different compression algorithms.
In general investigating and solving problems related to compressed archives with a programmatic approach it's likely to be a tricky and long task.
Try this with your command.
I tried using ZipInputStream and ZipOutputStream. But ZipInputStream always failed at some point when doing: "getNextEntry()". Basically the following lines of code in "getNextEntry()":
...
if ((entry = readLOC()) == null) {
return null;
}
...
returned null after some entries and I could not get further.
But finally I could solve the issue using ZipFile together with ZipOutputStream because ZipFile was reading all zip entries without problem and the solution looks like this:
protected void repairZipFile(String file) throws IOException {
File repairZipFile = new File(file+".repair");
ZipFile zipFile = new ZipFile(file);
Enumeration<? extends ZipEntry> zipFileEntries = zipFile.entries();
InputStream zis;
ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(repairZipFile));
byte[] b = new byte[1024];
while(zipFileEntries.hasMoreElements()){
ZipEntry zipEntry = zipFileEntries.nextElement();
zos.putNextEntry(zipEntry);
zis = zipFile.getInputStream(zipEntry);
int n = zis.read(b);
while(n>=0) {
zos.write(b, 0, n);
n = zis.read(b);
}
zis.close();
zos.closeEntry();
}
zipFile.close();
zos.flush();
zos.close();
Files.move(repairZipFile.toPath(), (new File(file)).toPath(), StandardCopyOption.REPLACE_EXISTING);
}
There are two ways to open ZIP files in Java, using the ZipFile class, or using ZipInputStream.
As far as I remember, ZipFile reads the central directory of a zip file first - it can do this because it uses a RandomAccessFile underneath. However, ZipInputStream uses the in-line entry information, which might be better if the central directory, which I think exists at the end of the file, is missing or corrupt.
So, it might be possible to 'repair' a ZIP file in Java by reading a ZIP file using ZipInputStream, and writing it back out to another file using a ZipOutputStream, copying entry information between them. You might end up getting IO exceptions reading from the last entry of the ZipInputStream if it got truncated, but it might still save the other previous entries from the file.

Categories

Resources