Zip Support

This Spring Integration module provides Zip (un-)compression support. A zipping algorithm implementation is based on a ZeroTurnaround ZIP Library. The following components are provided:

You need to include this dependency into your project:

  • Maven

  • Gradle

<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-zip</artifactId>
    <version>6.4.0</version>
</dependency>
compile "org.springframework.integration:spring-integration-zip:6.4.0"

Namespace Support

All components within the Spring Integration Zip module provide namespace support. In order to enable namespace support, you need to import the schema for the Spring Integration Zip Module. The following example shows a typical setup:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:int="http://www.springframework.org/schema/integration"
  xmlns:int-zip="http://www.springframework.org/schema/integration/zip"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
    https://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/integration
    https://www.springframework.org/schema/integration/spring-integration.xsd
    http://www.springframework.org/schema/integration/zip
    https://www.springframework.org/schema/integration/zip/spring-integration-zip.xsd">
</beans>

(Un)Zip Transformer

The ZipTransformer implements a zipping functionality for these types of input payload: File, String, byte[] and Iterable. In input data types can be mixed as part of an Iterable. For example, it should be easily to compress a collection containing Strings, byte arrays and Files. It is important to note that nested Iterables are NOT SUPPORTED at present time.

The ZipTransformer can be customized by setting several properties:

  • compressionLevel - sets the compression level. Default is Deflater#DEFAULT_COMPRESSION.

  • useFileAttributes - specifies whether the name of the file shall be used for the zip entry.

  • fileNameGenerator - uses to generate an original file name based on the request message. Defaults to DefaultFileNameGenerator. The .zip extension is added into this name for the target zip file name. Unless it is already present as a result of this generator.

In addition, a ZipHeaders.ZIP_ENTRY_FILE_NAME and ZipHeaders.ZIP_ENTRY_LAST_MODIFIED_DATE can be supplied for the name of zip entry and its lastmodified attribute. If not provided, the entry name is exact result of the fileNameGenerator and lastmodified falls back to the current date and time. If the payload of request message is an Iterable, then this entry name is modified with an index starting 1.

For example to zip a simple test.txt file into a test.txt.zip, only this configuration is enough:

  • Java DSL

  • Kotlin DSL

  • Groovy DSL

  • Java

  • XML

@Bean
public IntegrationFlow zipFlow() {
    return IntegrationFlow
             .from("zipChannel")
             .transform(new ZipTransformer())
             .get();
}
@Bean
fun zipFlow() =
    integrationFlow("zipChannel") {
        transform(ZipTransformer())
    }
@Bean
zipFlow() {
    integrationFlow 'zipChannel',
            {
                transform new ZipTransformer()
            }
}
@Transformer(inputChannel = "zipChannel")
@Bean
ZipTransformer zipTransformer() {
    return new ZipTransformer();
}
<int-zip:zip-transformer input-channel="zipChannel"/>

See ZipTransformer Javadocs for more information.

An UnZipTransformer supports these of input payload: File, byte[] and InputStream. When unzipping data, an expectSingleResult property can be specified. If set to true and more than 1 zip entry were detected, a MessagingException will be raised. This property also influences the return type of the payload. If set to false (default), then the payload will be of type SortedMap, if true, however, the actual zip entry will be returned.

Other properties that can be set on the UnZipTransformer:

  • deleteFiles - if the payload is an instance of File, this property specifies whether to delete the File after transformation. Default is false.

  • ZipResultType - defines the format of the data returned after transformation. Available options are: File, byte[].

  • workDirectory - the work directory is used when a ZipResultType is set to ZipResultType.FILE. By default, this property is set to the System temporary directory containing a subdirectory ziptransformer.

For example to zip a simple test.zip file into a map of its entries, only this configuration is enough:

  • Java DSL

  • Kotlin DSL

  • Groovy DSL

  • Java

  • XML

@Bean
public IntegrationFlow unzipFlow() {
    return IntegrationFlow
             .from("unzipChannel")
             .transform(new UnZipTransformer())
             .get();
}
@Bean
fun unzipFlow() =
    integrationFlow("unzipChannel") {
        transform(UnZipTransformer())
    }
@Bean
unzipFlow() {
    integrationFlow 'unzipChannel',
            {
                transform new UnZipTransformer()
            }
}
@Transformer(inputChannel = "unzipChannel")
@Bean
UnZipTransformer unzipTransformer() {
    return new UnZipTransformer();
}
<int-zip:unzip-transformer input-channel="unzipChannel"/>

Unzipped Splitter

The UnZipResultSplitter is useful in cases where zip files contain more than 1 entry. Essentially it has to be used as the next step in the integration flow after the mentioned above UnZipTransformer. It supports only a Map as an input data and emits every entry into an outputChannel with FileHeaders.FILENAME and ZipHeaders.ZIP_ENTRY_PATH headers.

The following example demonstrates a simple configuration for splitting unzipped result:

  • Java DSL

  • Kotlin DSL

  • Groovy DSL

  • Java

  • XML

@Bean
public IntegrationFlow unzipSplitFlow(Executor executor) {
    return IntegrationFlow
             .from("unzipChannel")
             .transform(new UnZipTransformer())
             .split(new UnZipResultSplitter())
             .channel(c -> c.executor("entriesChannel", executor))
             .get();
}
@Bean
fun unzipFlow(executor: Executor) =
    integrationFlow("unzipChannel") {
        transform(UnZipTransformer())
        split(UnZipResultSplitter())
        channel { executor("entriesChannel", executor) }
    }
@Bean
unzipFlow(Executor executor) {
    integrationFlow 'unzipChannel',
            {
                transformWith {
                    ref new UnZipTransformer()
                }
                splitWith {
                    ref new UnZipResultSplitter()
                }
                channel { executor 'entriesChannel', executor }
            }
}
@Transformer(inputChannel = "unzipChannel", outputChannel = "splitChannel")
@Bean
UnZipTransformer unzipTransformer() {
    return new UnZipTransformer();
}

@Spitter(inputChannel = "splitChannel", outputChannel = "entriesChannel")
@Bean
UnZipResultSplitter unZipSplitter() {
    return new UnZipResultSplitter();
}

@Bean
ExecutorChannel entriesChannel(Executor executor) {
    return new ExecutorChannel(executor);
}
<int:chain input-channel="unzipChannel" output-channel="entriesChannel">
    <int-zip:unzip-transformer/>
    <int:splitter>
        <bean class="org.springframework.integration.zip.splitter.UnZipResultSplitter"/>
    </int:splitter>
</int:chain>

<int:channel id="entriesChannel">
    <int:dispatcher task-executor="executor"/>
</int:channel>