Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Download zip InputStream Struts2 Java

First, sorry for english. I wanna know if you can help me to resolve this problem. What I'm trying is to download a zip that I have create from multiple byte[] using Struts2 and stream result.

I use ZipOutputStream and I have managed to create a File from it and read and download it using FileInputStream, but my problem is that I don't wan't to create a File. I just wanna convert the ZipOutputStream into InputStream (for example into ZipIntputStream) and download that ZipInputStream. For doing this I use this code:

public void downloadZip() {
    contentType = "application/octet-stream";
    filename="myZip.zip";
    ByteArrayOutputStream baos = new ByteArrayOutputStream();       
    byte[] bytes;
    try {
        ZipOutputStream zos = new ZipOutputStream(baos);
        ZipEntry ze;
        bytes = otherClass.getBytes("File1");
        ze = new ZipEntry("File1.pdf");
        zos.putNextEntry(ze);
        zos.write(bytes);
        zos.closeEntry();

        bytes = otherClass.getBytes("File2");
        ze = new ZipEntry("File2.pdf");
        zos.putNextEntry(ze);
        zos.write(bytes);
        zos.closeEntry();

        zos.flush();
        inputStream = new ZipInputStream(new ByteArrayInputStream(baos.toByteArray()));
        zos.close();
}
catch(Exception e){...}
}

My action struts.xml

<action...>
    <result name="success" type="stream">
        <param name="contentType">${contentType}</param>
        <param name="inputName">inputStream</param>
        <param name="contentDisposition">attachment;filename="${filename}"</param>
        <param name="bufferSize">1024</param>
    </result>
</action>

The problem is that the browser shows me a message says to me that the file it's not a valid zip, and its size is 0 bytes.

I hope I explained clearly, thanks a lot in advance.

Edit: As I have commented, finally I get the solution and it's very similar to leonbloy's reply. Besides return the ByteArrayInputStream I should close the ZipOutputStream before create the ByteArrayInputStream. Here's the result code, maybe it can be useful for other people:

    ...
    zos.closeEntry();                       
    zos.close();            
    inputStream = new ByteArrayInputStream(baos.toByteArray());
}
catch(Exception e){...}
}

Thanks for your help.

like image 852
Airenin Avatar asked Nov 23 '25 15:11

Airenin


2 Answers

The parameter <param name="inputName">inputStream</param> tells Struts2 from where to get the raw bytes that will be sent to the client. In your case, you want to send the zipped bytes. Instead, you are setting inputStream=ZipInputStream , which is a stream that takes a zipped source - to unzip sit. You don't want that, you want to send the raw zipped bytes.

REplace then

inputStream = new ZipInputStream(new ByteArrayInputStream(baos.toByteArray()))

by

inputStream = new ByteArrayInputStream(baos.toByteArray())

and it should work

like image 182
leonbloy Avatar answered Nov 26 '25 05:11

leonbloy


You are lucky, this is exactly what you need.

In that specific case, I've bypassed the framework result system by writing directly to the response. That way, the Zip will be created immediately in the client system, and it will be feeded progressively, instead of waiting for the end of the elaboration and outputting it all at once with Stream result.

Then no result defined:

<action name="createZip" class="foo.bar.CreateZipAction" />

And in the Action:

public String execute() {
    try {        
        /* Read the amount of data to be streamed from Database to File System,
           summing the size of all Oracle's BLOB, PostgreSQL's ABYTE etc: 
           SELECT sum(length(my_blob_field)) FROM my_table WHERE my_conditions
        */          
        Long overallSize = getMyService().precalculateZipSize();

        // Tell the browser is a ZIP
        response.setContentType("application/zip"); 
        // Tell the browser the filename, and that it needs to be downloaded instead of opened
        response.addHeader("Content-Disposition", "attachment; filename=\"myArchive.zip\"");        
        // Tell the browser the overall size, so it can show a realistic progressbar
        response.setHeader("Content-Length", String.valueOf(overallSize));      

        ServletOutputStream sos = response.getOutputStream();       
        ZipOutputStream zos = new ZipOutputStream(sos);

        // Set-up a list of filenames to prevent duplicate entries
        HashSet<String> entries = new HashSet<String>();

        /* Read all the ID from the interested records in the database, 
           to query them later for the streams: 
           SELECT my_id FROM my_table WHERE my_conditions */           
        List<Long> allId = getMyService().loadAllId();

        for (Long currentId : allId){
            /* Load the record relative to the current ID:         
               SELECT my_filename, my_blob_field FROM my_table WHERE my_id = :currentId            
               Use resultset.getBinaryStream("my_blob_field") while mapping the BLOB column */
            FileStreamDto fileStream = getMyService().loadFileStream(currentId);

            // Create a zipEntry with a non-duplicate filename, and add it to the ZipOutputStream
            ZipEntry zipEntry = new ZipEntry(getUniqueFileName(entries,fileStream.getFilename()));
            zos.putNextEntry(zipEntry);

            // Use Apache Commons to transfer the InputStream from the DB to the OutputStream
            // on the File System; at this moment, your file is ALREADY being downloaded and growing
            IOUtils.copy(fileStream.getInputStream(), zos);

            zos.flush();
            zos.closeEntry();

            fileStream.getInputStream().close();                    
        }

        zos.close();
        sos.close();    

    } catch (Exception e) {
        logError(e);
    finally {
        IOUtils.closeQuietly(sos);
    }

    return NONE;
}
like image 39
Andrea Ligios Avatar answered Nov 26 '25 05:11

Andrea Ligios



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!