Search code examples
stringgroovyzipbyte

Groovy: Convert Zip content bytes to string


I have these 2 classes:

  1. Message class
  2. MessageLog class

The first class Message, has a method getBody which can return an object that we can specify (as in the documentation), and the second class MessageLog has a method addAttachmentAsString which take the 2nd parameter as String as the content of the attachment.

Now, I have a Zip content in the Message.body, I can use the getBody method to get the byte content, then I want to pass to the second class's method, which is MessageLog.addAttachmentAsString. The content is to be passed to the second parameter and it only takes String.

I have tried the following code:

messageLog.addAttachmentAsString("attachment_name", message.getBody(String), "application/zip")

But the result is, I can download the attachment, but the zip content is corrupted and cannot be opened.

Is it possible to convert the zip content, which is in bytes and pass in String without corrupting the content? Thank you.


Solution

  • So Zip files are binary. Strings are an interpretation of binary data using a character encoding format (ASCII, UTF-8, UTF-24, etc) to be readable. Not all binary are Strings, but all Strings are binary. Zip files are not interpretable as a Strings because they don't follow the rules of any character encoding.

    However, there is a way to represent binary data as text using Base64 encoding. So, if you base64 encode your zip file attachment then you could return it as a String, BUT in order to unzip the file you'd need to first decode the base64 to get back to binary before you then unzipped it.

    From your post you didn't mention anything about email or base64 encoding so it's hard to know if that's what was tripping you up or whomever implemented that method didn't use base64 and it's just garbling it.

    A base64 file isn't readable without decoding it back to the underlying binary. Here is an example of encoding and decoding in Base64:

    import java.util.zip.*
    // create a zip file with one text file inside it.
    File zip = new File("output.zip")
    zip.withOutputStream { out ->
       ZipOutputStream zos = new ZipOutputStream( out )
       zos.putNextEntry( new ZipEntry("output.txt"))
       zos.write("'ello potato cakes!  This is a string we're going to write to the zip file.".getBytes("UTF-8"))
       zos.closeEntry()
       zos.flush()
       zos.close()
    }
    
    // Base64 encode the zip file
    String base64Zip = zip.bytes.encodeBase64().toString()
    
    println("Encoded zip as base64")
    println( base64Zip )
    
    println("Decoding base64")
    
    // read the zip file by first base64 decoding it, and reading it as a ZipInputStream.
    new ZipInputStream( new ByteArrayInputStream( base64Zip.decodeBase64() ) ).withStream { zin ->
       ZipEntry entry = null
       while( (entry = zin.nextEntry) != null ) {
          println( entry.name )
          ByteArrayOutputStream baos = new ByteArrayOutputStream()
          baos << zin
          println( baos.toString("UTF-8") )
       }
    }