Skip to content

Instantly share code, notes, and snippets.

@BaptisteGarcin
Created December 11, 2020 14:57
Show Gist options
  • Save BaptisteGarcin/74b36d451aeacd0cac2dd5d2e9c0e98b to your computer and use it in GitHub Desktop.
Save BaptisteGarcin/74b36d451aeacd0cac2dd5d2e9c0e98b to your computer and use it in GitHub Desktop.
InputStream to .zip in kotlin (GZIPInputStream, GZIPOutputStream + PipedInputStream, PipedOutputStream)
// Inspired from https://gist.github.com/xis19/41999145269e4aa398aa354e129e507c
// Use externally with GzipUtils.compress(yourInputStream)
@Component
object GzipUtils {
private const val BUFFER_SIZE_BYTES = 512 * 1024
private val gzipExecutor = Executors.newCachedThreadPool(CustomizableThreadFactory("gzip-thread-"))
@Volatile
private var isShuttingDown = false
/**
* Compresses the input data using GZip and outputs the compressed data.
*
* @param input
* An [InputStream] containing the input raw data.
*
* @return An [InputStream] to the compressed data.
*/
fun compress(input: InputStream): InputStream {
checkShutdown()
val compressedDataStream = PipedInputStream(BUFFER_SIZE_BYTES)
gzipExecutor.submit {
logger.debug("About to compress input data using gzip asynchronously...")
val compressionOutput = PipedOutputStream(compressedDataStream)
val gzipout: OutputStream = object : GZIPOutputStream(compressionOutput) {
init {
def.setLevel(Deflater.BEST_COMPRESSION)
}
}
try {
input.copyTo(gzipout, BUFFER_SIZE_BYTES)
logger.error("Successfully compressed input data using gzip.")
} catch (e: IOException) {
logger.error("Failed to compress input data.", e)
} finally {
try {
gzipout.close()
} catch (e: IOException) {
logger.error("Failed to close gzip output stream.", e)
}
}
}
return compressedDataStream
}
/**
* Decompresses the input data using GZip and outputs the decompressed data.
*
* @param input
* An [InputStream] containing the input compressed data.
*
* @return An [InputStream] to the decompressed raw data.
*
* @throws IOException
* Error during decompression
*/
@Throws(IOException::class)
fun decompress(input: InputStream): InputStream {
checkShutdown()
val decompressedDataStream = PipedInputStream()
val decompressionOutput = PipedOutputStream(decompressedDataStream)
gzipExecutor.submit {
logger.debug("About to decompress input data using gzip asynchronously...")
val gzipCompressedDataStream = GZIPInputStream(input)
try {
gzipCompressedDataStream.copyTo(decompressionOutput, BUFFER_SIZE_BYTES)
logger.debug("Successfully decompressed input data using gzip.")
} catch (e: IOException) {
logger.error("Failed to decompress input data.", e)
} finally {
try {
decompressionOutput.close()
} catch (e: IOException) {
logger.error("Failed to close piped output stream.", e)
}
try {
gzipCompressedDataStream.close()
} catch (e: IOException) {
logger.error("Failed to close gzip input stream.", e)
}
}
}
return decompressedDataStream
}
private fun checkShutdown() {
if (isShuttingDown) {
throw RejectedExecutionException("Gzip compression/decompression executor has shutdown.")
}
}
@PreDestroy
fun shutdown() {
if (gzipExecutor.isShutdown) {
return
}
isShuttingDown = true
gzipExecutor.shutdownNow()
try {
gzipExecutor.awaitTermination(30, TimeUnit.SECONDS)
logger.debug("Gzip compression/decompression executor has shutdown successfully.")
} catch (e: InterruptedException) {
logger.error("Waiting for gzip compression/decompression executor shutting down has been interrupted.", e)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment