Creating ZIP file in memory - java

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).

Related

How to create an instance of java.io.File from a byte[] without saving the file to the disk

I must use an existing method: method.invoke(myClassLoader, myFile.toURI().toURL()); where myFile is an instance of File and I need to create it from a byte[] without saving the file on the disk, is this possible?
This is what I tried but it creates a file on the disk and writes it:
byte[] bytes = ...;
File tempFile = File.createTempFile("prefix", "suffix");
FileOutputStream fos = new FileOutputStream(tempFile);
fos.write(bytes);
method.invoke(myClassLoader, tempFile.toURI().toURL());
The parameter of the invoked method is URL. You can use Jimfs (An in-memory file system for Java 7+) to create a file emulation in memory and get its URL (path.toUri().toURL()).

How can I transform an uncompressed file into zipped bytes?

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().

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

Sending file through ObjectOutputStream and then saving it in Java?

I have this simple Server/Client application. I'm trying to get the Server to send a file through an OutputStream (FileOutputStream, OutputStream, ObjectOutputStream, etc) and receive it at the client side before saving it into an actual file. The problem is, I've tried doing this but it keeps failing. Whenever I create the file and write the object I received from the server into it, I get a broken image (I just save it as a jpg, but that shouldn't matter). Here are the parts of the code which are most likely to be malfunctioning (all the seemingly un-declared objects you see here have already been declared beforehand):
Server:
ObjectOutputStream outToClient = new ObjectOutputStream(
connSocket.getOutputStream());
File imgFile = new File(dir + children[0]);
outToClient.writeObject(imgFile);
outToClient.flush();
Client:
ObjectInputStream inFromServer = new ObjectInputStream(
clientSocket.getInputStream());
ObjectOutputStream saveImage = new ObjectOutputStream(
new FileOutputStream("D:/ServerMapCopy/gday.jpg"));
saveImage.writeObject(inFromServer.readObject());
So, my problem would be that I can't get the object through the stream correctly without getting a broken file.
A File object represents the path to that file, not its actual content. What you should do is read the bytes from that file and send those over your ObjectOutputStream.
File f = ...
ObjectOutputStream oos = ...
byte[] content = Files.readAllBytes(f.toPath);
oos.writeObject(content);
File f=...
ObjectInputStream ois = ...
byte[] content = (byte[]) ois.readObject();
Files.write(f.toPath(), content);
You are not actually transferring the file, but the File instance from Java. Think of your File object as a (server-)local handle to the file, but not its contents. For transferring the image, you'd actually have to read it on the server first.
However, if you're just going to save the bytes on the client anyway, you can forget about the ObjectOutputStream to begin with. You can just transfer the bytes stored in the File. Take a look at the FileChannel class and its transferTo and transferFrom methods as a start.

Categories

Resources