Skip to content

Instantly share code, notes, and snippets.

@xis19
Created November 18, 2019 06:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xis19/41999145269e4aa398aa354e129e507c to your computer and use it in GitHub Desktop.
Save xis19/41999145269e4aa398aa354e129e507c to your computer and use it in GitHub Desktop.
Demo of GZIPInputStream, GZIPOutputStream + PipedInputStream, PipedOutputStream
import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import javax.annotation.PreDestroy;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.concurrent.CustomizableThreadFactory;
import org.springframework.stereotype.Component;
@Component
public class GzipUtils {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private static final int BUFFER_SIZE_BYTES = 512 * 1024;
private final ExecutorService gzipExecutor = Executors.newCachedThreadPool(new CustomizableThreadFactory("gzip-thread-"));
private volatile boolean isShuttingDown = false;
/**
* Compresses the input data using GZip and outputs the compressed data.
*
* @param input
* An {@link InputStream} containing the input raw data.
*
* @return An {@link InputStream} to the compressed data.
*/
public InputStream compress(final InputStream input) {
this.checkShutdown();
final PipedInputStream compressedDataStream = new PipedInputStream(BUFFER_SIZE_BYTES);
this.gzipExecutor.submit(() -> {
logger.debug("About to compress input data using gzip asynchronously...");
PipedOutputStream compressionOutput;
GZIPOutputStream gzipCompressedDataStream = null;
try {
compressionOutput = new PipedOutputStream(compressedDataStream);
gzipCompressedDataStream = new GZIPOutputStream(compressionOutput);
IOUtils.copy(input, gzipCompressedDataStream, BUFFER_SIZE_BYTES);
logger.error("Successfully compressed input data using gzip.");
} catch (IOException e) {
logger.error("Failed to compress input data.", e);
} finally {
if (gzipCompressedDataStream != null) {
try {
gzipCompressedDataStream.close();
} catch (IOException e) {
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 {@link InputStream} containing the input compressed data.
*
* @return An {@link InputStream} to the decompressed raw data.
*
* @throws IOException
* Error during decompression
*/
public InputStream decompress(final InputStream input) throws IOException {
this.checkShutdown();
final PipedInputStream decompressedDataStream = new PipedInputStream();
final PipedOutputStream decompressionOutput = new PipedOutputStream(decompressedDataStream);
this.gzipExecutor.submit(() -> {
logger.debug("About to decompress input data using gzip asynchronously...");
GZIPInputStream gzipCompressedDataStream = null;
try {
gzipCompressedDataStream = new GZIPInputStream(input);
IOUtils.copy(gzipCompressedDataStream, decompressionOutput, BUFFER_SIZE_BYTES);
logger.debug("Successfully decompressed input data using gzip.");
} catch (IOException e) {
logger.error("Failed to decompress input data.", e);
} finally {
try {
decompressionOutput.close();
} catch (IOException e) {
logger.error("Failed to close piped output stream.", e);
}
if (gzipCompressedDataStream != null) {
try {
gzipCompressedDataStream.close();
} catch (IOException e) {
logger.error("Failed to close gzip input stream.", e);
}
}
}
});
return decompressedDataStream;
}
private void checkShutdown() {
if (this.isShuttingDown) {
throw new RejectedExecutionException("Gzip compression/decompression executor has shutdown.");
}
}
@PreDestroy
public void shutdown() {
if (this.gzipExecutor.isShutdown()) {
return;
}
this.isShuttingDown = true;
this.gzipExecutor.shutdownNow();
try {
this.gzipExecutor.awaitTermination(30, TimeUnit.SECONDS);
logger.info("Gzip compression/decompression executor has shutdown successfully.");
} catch (InterruptedException e) {
logger.error("Waiting for gzip compression/decompression executor shutting down has been interrupted.", e);
}
}
}
import org.springframework.core.io.ClassPathResource
import org.springframework.core.io.Resource
import spock.lang.Specification
class GzipUtilsTest extends Specification {
GzipUtils gzipUtils = new GzipUtils()
Resource bigTestData = new ClassPathResource("gzip-test-file.json", this.class)
def cleanup() {
gzipUtils.shutdown()
}
void "Given a string as raw data, when compress and decompress, decompressed data should be exactly the same as the raw data."() {
given:
String rawDataString = """Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed at ullamcorper tortor, vulputate faucibus turpis. Maecenas et tellus dignissim, efficitur turpis quis, sodales orci. Quisque tristique faucibus interdum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Cras id massa nisl. Aliquam aliquet sollicitudin turpis vel tincidunt. Aliquam ut velit vehicula, malesuada enim sed, fermentum nibh. In facilisis lacus sed eros convallis, at pharetra quam tempus. Donec euismod, tellus sed dictum bibendum, ex lorem maximus dui, nec rutrum erat sem vel ligula. Maecenas a mauris in dui eleifend auctor. Maecenas ex magna, malesuada sagittis ligula luctus, suscipit sagittis leo. Sed blandit tincidunt felis non aliquet."""
when:
ByteArrayInputStream input = new ByteArrayInputStream(rawDataString.bytes)
InputStream compressedData = gzipUtils.compress(input)
then:
noExceptionThrown()
when:
InputStream decompressedData = gzipUtils.decompress(compressedData)
then:
noExceptionThrown()
String decompressedDataString = decompressedData.text
decompressedDataString == rawDataString
}
void "Given a large file as raw data, when compress and decompress, decompressed data should be exactly the same as the raw data."() {
given:
String rawDataString = this.bigTestData.inputStream.text;
when:
InputStream compressedData = gzipUtils.compress(this.bigTestData.inputStream)
then:
noExceptionThrown()
when:
InputStream decompressedData = gzipUtils.decompress(compressedData)
then:
noExceptionThrown()
String decompressedDataString = decompressedData.text
decompressedDataString == rawDataString
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment