Created
November 18, 2019 06:32
-
-
Save xis19/41999145269e4aa398aa354e129e507c to your computer and use it in GitHub Desktop.
Demo of GZIPInputStream, GZIPOutputStream + PipedInputStream, PipedOutputStream
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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