Return Zip File from ZipOutputStream in Java

10,213

Solution 1

You should be able to use a ByteArrayOutputStream instead of a FileOutputStream:

zipOutputStream = new ZipOutputStream(new ByteArrayOutputStream());

The difficulty here is to provide a File to the method consuming the zip file. The java.io.File does not provide an abstraction which allows you to manipulate in-memory files.

The java.io.File abstraction and java.io.FileInputStream implementation

To simplify, if we had to boil down what the File abstraction is, we would see it as a URI. And therefore, to be able to build an in-memory File, or at least mimic it, we would need to provide an URI which would then be used by the consumer of the File to read its content.

If we look at the FileInputStream which the consumer is likely to use, we can see that it always ends up with a native call which gives us to possibility whatsoever to abstract a FileSystem for in-memory files:

// class java.io.FileInputStream
/**
 * Opens the specified file for reading.
 * @param name the name of the file
 */
private native void open0(String name) throws FileNotFoundException;

It would be easier if there was a possibility to adapt the consumer to accept an InputStream, but from your problem statement I guess this is not possible.

API call

Your requirement is to provide a File to the Watson Visual API. Could you please provide the API method you need to call?

Solution 2

public void compressFileList(List<File> fileList, OutputStream outputStream)
        throws IOException {
    try (ZipOutputStream zipOutputStream =
            new ZipOutputStream(new BufferedOutputStream(outputStream));
        for (File file: fileList) {
            try (FileInputStream fileInputStream = new FileInputStream(file)) {
                ZipEntry zipEntry = new ZipEntry(file.getName());
                zipOutputStream.putNextEntry(zipEntry);
                byte[] tmp = new byte[4*1024];
                int size = 0;
                while((size = fileInputStream.read(tmp)) != -1){
                    zipOutputStream.write(tmp, 0, size);
                }
                zipOutputStream.flush();
            } catch (FileNotFoundException e) { // Maybe skip not found files.
                Logger.log(Level.INFO, "File not found {}", file.getPath());
            }
        }
    }
}

Usage:

if (fileList.isEmpty()) {
    ...
    return;
}
try {
    compressFileList(fileList, servletRequest.getOutputStream())) {
} catch (FileNotFoundException e) {
   ...
} catch (IOException e) {
    ...
}
Share:
10,213
Vini
Author by

Vini

I started using SO in 2014 during my first development job in C#. Ever since I have been reading and learning a lot from SO. I am person with great hopes in Entrepreneurship but not forgetting the development skills that I have learned so far and still learning. I always am open to opportunities to meet people and learn new things. I always love the moment when I can think, "I would have never thought about it this way". Technology and people never cease to amaze me.

Updated on June 25, 2022

Comments

  • Vini
    Vini almost 2 years

    I have a function which creates a Zip file from a list of files. Is it possible to return the Zip file without it being saved on the disk? I need the file as I have to use the zip file as a parameter for another function. I am not sure of the ByteStream would be an option for me.

    public File compressFileList(List<File> fileList,String fileName) {
        FileOutputStream fileOutputStream=null;
        ZipOutputStream zipOutputStream=null;
        FileInputStream fileInputStream=null;
        String compressedFileName=fileName +".zip";
        if(fileList.isEmpty())
            return null;
        try
        {
            fileOutputStream =  new FileOutputStream(compressedFileName);
            zipOutputStream = new ZipOutputStream(new BufferedOutputStream(fileOutputStream));
            for (File file: fileList) {
                fileInputStream = new FileInputStream(file);
                ZipEntry zipEntry =  new ZipEntry(file.getName());
                zipOutputStream.putNextEntry(zipEntry);
                byte[] tmp = new byte[4*1024];
                int size = 0;
                while((size = fileInputStream.read(tmp)) != -1){
                    zipOutputStream.write(tmp, 0, size);
                }
                zipOutputStream.flush();
                fileInputStream.close();
            }
            zipOutputStream.close();
            return compressedFile; //This is what I am missing
    
        }
        catch (FileNotFoundException e)
        {
    
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
    

    EDIT : adding the use case

    The idea is to create a zip file and use the CreateClassifierOptions method of VisualRecognition Service of Watson.

    classifierOptions = new CreateClassifierOptions.Builder()
                .classifierName("Santa")
                .addClass("Santa", new File("C:\\app\\GitRepo\\images\\beagle.zip"))
                .negativeExamples(new File("C:\\app\\GitRepo\\images\\nosport.zip"))
                .build();
    

    The builder accepts the zip file as the parameter.

    Understanding

    Based on the explanation from Alexandre Dupriez, I think it is better to store the file at some place on the hard disk.

    • Lino
      Lino over 6 years
      what does the other function accept as an Input? An Inputstream?
    • Vini
      Vini over 6 years
      I am using WatsonService API, which accepts a zip file as input. or maybe i should create a temp zip file and return it to the function
    • Rob Obdeijn
      Rob Obdeijn over 6 years
      Your question conflicts with your method signature. A File object is basically a reference to a file stored in some path, but your question seems to be about returning the (byte) contents of a file. So if you don't want to store your file somewhere, then you shouldn't be returning a File object.
    • Vini
      Vini over 6 years
      Should I create a temporary file on the disk and send it to function?
    • Rob Obdeijn
      Rob Obdeijn over 6 years
      What are you trying to achieve from a functional perspective? Storing a temporary file and returning a reference to it is an option, but it depends on what you want to do with that file.
    • Vini
      Vini over 6 years
      I want to use the Watson Visual API. The Visual API uses a zip file as argument. I have decided to store the file in a temporary folder and use it to call the Visual API.
  • Vini
    Vini over 6 years
    I have mentioned that I want the above function to return a File. The calling function accepts only a Zip file as input.
  • Vini
    Vini over 6 years
    It doesnt return a file. I am looking for an option to return a zip file. I have reallized that i will have to save the file temporarily before I could use it for the next function.
  • Alexandre Dupriez
    Alexandre Dupriez over 6 years
    Yes, please - wonder what could be done and if there is a workaround
  • Vini
    Vini over 6 years
    I have added the use case. I assume that it is better to save the file and then call the watson service.
  • Joop Eggen
    Joop Eggen over 6 years
    Or (as int my answer) you can pass an OutputStream for delivery of the zip file. Then you do not need to maintain a byte array with the zip files content.