0

In other words I have:

InputStream inputStream = getInputStreamFromSource();
byte[] output = zipOutputStreamAndConvertToByteArray(inputStream);

How would the function zipOutputStreamAndConvertToByteArray be implemented?

private byte[] zipOutputStreamAndConvertToByteArray(InputStream inputStream)
{
    // what code goes here?
}
Stephane Grenier
  • 14,426
  • 35
  • 98
  • 179
  • 1
    https://docs.oracle.com/javase/8/docs/api/java/util/zip/ZipInputStream.html – JB Nizet Apr 07 '18 at 09:07
  • You're assuming the inputstream is zipped which it's not in my case. I'm going from an inputstream to a byte[] and would like to compress the input stream before stuffing it into the byte[] because it's not compressed (and I need the final format to be a byte[] – Stephane Grenier Apr 07 '18 at 09:08
  • Sorry, I meant to link to ZipOutputStream. – JB Nizet Apr 07 '18 at 09:13
  • The [`Deflater`](https://docs.oracle.com/javase/8/docs/api/java/util/zip/Deflater.html) class has a good example in its javadoc. – Izruo Apr 07 '18 at 09:14
  • That's what I'm trying to do but it's not working. I'm even stuffing a ByteArrayOutputStream into the ZipOutputStream but the byte array is empty – Stephane Grenier Apr 07 '18 at 09:15
  • @Izruo the link doesn't work – Stephane Grenier Apr 07 '18 at 09:16
  • @StephaneGrenier Yes, somehow the last 'l' got duplicated; I edited it. – Izruo Apr 07 '18 at 09:16
  • You're also starting with a byte[] and not an inputstream. – Stephane Grenier Apr 07 '18 at 09:18
  • @StephaneGrenier It's not my example, after all. You would have to loop it along with a `inputStream.read(input)`. – Izruo Apr 07 '18 at 09:19
  • @StephaneGrenier when you ask a question, **post what you have tried**. With 8046 rep points you should know that. – JB Nizet Apr 07 '18 at 09:20
  • I tried a dozen different things and nothing worked so I had no idea how far or close I was. I was basically trying to push it into the stream but completely forgot about the ZipEntry code... – Stephane Grenier Apr 07 '18 at 09:22
  • It looks like you want us to write some code for you. While many users are willing to produce code for a coder in distress, they usually only help when the poster has already tried to solve the problem on their own. A good way to demonstrate this effort is to include the code you've written so far, example input (if there is any), the expected output, and the output you actually get (console output, tracebacks, etc.). The more detail you provide, the more answers you are likely to receive. Check the [FAQ] and [ask]. – lexicore Apr 07 '18 at 09:23
  • 1
    The problem is that sometimes you really don't know what the code should be. I will admit that input/output streams are my weakness, and as a result I'm not confident on how to move from one to the other. I was trying to stuff a ByteArrayOutputSteam into the ZipOutputStream which was failing, so I was nowhere close. I've also gone through 50+ websites and none of them were very helpful. It was always going the long way, read it all into a byte array, then write the file, re-read it, compress it, etc. Very hacky and terrible. – Stephane Grenier Apr 07 '18 at 09:25
  • Just an FYI for those with negative comments and downvoting the question, one of the higher ranking questions is in exactly this format: https://stackoverflow.com/questions/309424/read-convert-an-inputstream-to-a-string – Stephane Grenier Apr 07 '18 at 10:12
  • @StephaneGrenier don’t use ten year old questions as a guide line for Stackoverflow. The policies have changed over time and a lot of questions accepted back then wouldn’t today. Some of them are even marked as “kept for historical reasons” explicitly. – Holger Apr 09 '18 at 06:54

2 Answers2

4

This creates a zip file with file named file:

private static byte[] zipOutputStreamAndConvertToByteArray(InputStream inputStream) throws IOException {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    try (ZipOutputStream zip = new ZipOutputStream(outputStream)) {
        zip.putNextEntry(new ZipEntry("file"));
        try (InputStream in = inputStream) { 
        // this try block can be replaced with IOUtils.copy or ByteStreams.copy
            byte[] buffer = new byte[4096];
            int len;
            while ((len = in.read(buffer)) > 0) {
                zip.write(buffer, 0, len);
            }
        }
        zip.closeEntry();
    }

    return outputStream.toByteArray();
}
Adam Siemion
  • 14,533
  • 4
  • 47
  • 84
  • 2
    Note that this also creates a ZIP entry in the outputted data. To only compress the data without any overhead a [`DeflaterOutputStream`](https://docs.oracle.com/javase/8/docs/api/java/util/zip/DeflaterOutputStream.html) must be used. – Izruo Apr 07 '18 at 09:22
  • @Izruo in that case, you can do even better, use [`DeflaterInputStream`](https://docs.oracle.com/javase/9/docs/api/?java/util/zip/DeflaterInputStream.html) to compress right when reading, eliminating the need to deal with `ByteArrayOutputStream`. I’ve added [an answer](https://stackoverflow.com/a/49727844/2711488) showing the alternative. – Holger Apr 09 '18 at 07:45
4

Note that Java 9 allows to simplify the solution of Adam Siemion’s answer significantly:

private static byte[] zipIntoByteArray(InputStream inputStream) throws IOException {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    try(ZipOutputStream zip = new ZipOutputStream(outputStream); inputStream) {
        zip.putNextEntry(new ZipEntry("file"));
        inputStream.transferTo(zip);
    }
    return outputStream.toByteArray();
}

The first changes are not Java 9 specific, you can manage both streams with a single try statement and you don’t need to close the last (sole) zip entry as it will get closed automatically when the stream is closed. Then, Java 9 allows you to specify an existing variable like inputStream without declaring another variable in the try statement. Further, you can use transferTo to transfer all remaining data from an InputStream to an OutputStream, without the need to implement a copying routine.


As said by Izruo in this comment when you don’t need the data in the zip file format and only want to hold a single file, you can get rid of the zip file specific overhead by using the DEFLATE algorithm directly. We could do this similar to the solution above, just replacing ZipOutputStream with DeflaterOutputStream, but there’s another alternative, using DeflaterInputStream to compress the data right when reading:

private static byte[] compressIntoByteArray(InputStream inputStream) throws IOException {
    try(DeflaterInputStream deflate = new DeflaterInputStream(inputStream)) {
        return deflate.readAllBytes();
    }
}

When you have the array containing the compressed data, you can use new InflaterInputStream(new ByteArrayInputStream(array)) to get an InputStream decompressing on the fly, replicating the data of the original input stream.

Holger
  • 243,335
  • 30
  • 362
  • 661
  • wait... so you can add `inputStream` to `(ZipOutputStream zip = new ZipOutputStream(outputStream); inputStream)`? that is something I was not aware of. nice! – Eugene Apr 09 '18 at 13:26
  • 1
    @Eugene that’s the Java 9 feature, you don’t need a dummy variable, but can just write `try(inputStream) { … }` instead of `try(InputStream dummyVariable = inputStream) { … }`, if `inputStream` is effectively final. This new feature can be combined with the old feature of listing multiple resource in a single `try`. You could also write `try(inputStream; ZipOutputStream zip = new ZipOutputStream(outputStream)) { … }` instead. – Holger Apr 09 '18 at 13:28