[[zip]]
== Zip Support

This Spring Integration module provides https://en.wikipedia.org/wiki/ZIP_(file_format)[Zip] (un-)compression support.
A zipping algorithm implementation is based on a https://github.com/zeroturnaround/zt-zip[ZeroTurnaround ZIP Library].
The following components are provided:

* <<zip-transformer,Zip Transformer>>
* <<zip-transformer,UnZip Transformer>>
* <<unzip-splitter,UnZip Result Splitter>>

You need to include this dependency into your project:

====
[source, xml, subs="normal", role="primary"]
.Maven
----
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-zip</artifactId>
    <version>{project-version}</version>
</dependency>
----

[source, groovy, subs="normal", role="secondary"]
.Gradle
----
compile "org.springframework.integration:spring-integration-zip:{project-version}"
----
====

[[xpath-namespace-support]]
=== 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:

====
[source,xml]
----
<?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>
----
====

[[zip-transformer]]
=== (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.

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

====
[source, java, role="primary"]
.Java DSL
----
@Bean
public IntegrationFlow zipFlow() {
    return IntegrationFlow
             .from("zipChannel")
             .transform(new ZipTransformer())
             .get();
}
----
[source, kotlin, role="secondary"]
.Kotlin DSL
----
@Bean
fun zipFlow() =
    integrationFlow("zipChannel") {
        transform(ZipTransformer())
    }
----
[source, groovy, role="secondary"]
.Groovy DSL
----
@Bean
zipFlow() {
    integrationFlow 'zipChannel',
            {
                transform new ZipTransformer()
            }
}
----
[source, java, role="secondary"]
.Java
----
@Transfomer(inputChannel = "zipChannel")
@Bean
ZipTransformer zipTransformer() {
    return new ZipTransformer();
}
----
[source, xml, role="secondary"]
.XML
----
<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:

====
[source, java, role="primary"]
.Java DSL
----
@Bean
public IntegrationFlow unzipFlow() {
    return IntegrationFlow
             .from("unzipChannel")
             .transform(new UnZipTransformer())
             .get();
}
----
[source, kotlin, role="secondary"]
.Kotlin DSL
----
@Bean
fun unzipFlow() =
    integrationFlow("unzipChannel") {
        transform(UnZipTransformer())
    }
----
[source, groovy, role="secondary"]
.Groovy DSL
----
@Bean
unzipFlow() {
    integrationFlow 'unzipChannel',
            {
                transform new UnZipTransformer()
            }
}
----
[source, java, role="secondary"]
.Java
----
@Transfomer(inputChannel = "unzipChannel")
@Bean
UnZipTransformer unzipTransformer() {
    return new UnZipTransformer();
}
----
[source, xml, role="secondary"]
.XML
----
<int-zip:unzip-transformer input-channel="unzipChannel"/>
----
====

[[unzip-splitter]]
=== 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:

====
[source, java, role="primary"]
.Java DSL
----
@Bean
public IntegrationFlow unzipSplitFlow(Executor executor) {
    return IntegrationFlow
             .from("unzipChannel")
             .transform(new UnZipTransformer())
             .split(new UnZipResultSplitter())
             .channel(c -> c.executor("entriesChannel", executor))
             .get();
}
----
[source, kotlin, role="secondary"]
.Kotlin DSL
----
@Bean
fun unzipFlow(executor: Executor) =
    integrationFlow("unzipChannel") {
        transform(UnZipTransformer())
        split(UnZipResultSplitter())
        channel { executor("entriesChannel", executor) }
    }
----
[source, groovy, role="secondary"]
.Groovy DSL
----
@Bean
unzipFlow(Executor executor) {
    integrationFlow 'unzipChannel',
            {
                transform new UnZipTransformer()
                split new UnZipResultSplitter()
                channel { executor 'entriesChannel', executor }
            }
}
----
[source, java, role="secondary"]
.Java
----
@Transfomer(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);
}
----
[source, xml, role="secondary"]
.XML
----
<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>
----
====
