I am learning new framework called Micronaut. And I wanted to know how to upload the file and modify the configuration to allow multipart data, So I began googling and consolidate my learning in to single blog. This article  helps you to upload a file and download a file from micronut controller. People who are new to micronut is another JVM based frame work, which allows you to develop application which are lightweight modular, it enforces on reducing reflections and achieve low memory foot print.

As a web developer we may have to upload and download file at least in  any of our application. And this begins with Googling. During this process i have Documented Code snippet  which guides from start to finish.

Dependency :

I Have not added any extra dependencies, And also i have attached my pom for your reference.  

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.ashrithgn.demo</groupId>
    <artifactId>file</artifactId>
    <version>0.1</version>
    <packaging>${packaging}</packaging>

    <parent>
        <groupId>io.micronaut</groupId>
        <artifactId>micronaut-parent</artifactId>
        <version>2.3.1</version>
    </parent>

    <properties>
        <packaging>jar</packaging>
        <jdk.version>8</jdk.version>
        <release.version>8</release.version>
        <micronaut.version>2.3.1</micronaut.version>
        <exec.mainClass>com.ashrithgn.link_note.news.Application</exec.mainClass>
        <micronaut.runtime>lambda</micronaut.runtime>
    </properties>

    <repositories>
        <repository>
            <id>central</id>
            <url>https://repo.maven.apache.org/maven2</url>
        </repository>
        <repository>
            <id>jcenter.bintray.com</id>
            <url>https://jcenter.bintray.com</url>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>io.micronaut</groupId>
            <artifactId>micronaut-inject</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>io.micronaut</groupId>
            <artifactId>micronaut-validation</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>io.micronaut</groupId>
            <artifactId>micronaut-http-client</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.micronaut</groupId>
            <artifactId>micronaut-runtime</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>io.micronaut.aws</groupId>
            <artifactId>micronaut-function-aws-api-proxy</artifactId>
            <scope>compile</scope>
        </dependency>
        <dependency>
            <groupId>io.micronaut.aws</groupId>
            <artifactId>micronaut-function-aws-api-proxy-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.micronaut.test</groupId>
            <artifactId>micronaut-test-junit5</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>

        <dependency>
            <groupId>io.micronaut.servlet</groupId>
            <artifactId>micronaut-http-server-jetty</artifactId>
            <version>1.0.2</version>
        </dependency>


    </dependencies>


    <build>
        <finalName>news</finalName>
        <plugins>
            <plugin>
                <groupId>io.micronaut.build</groupId>
                <artifactId>micronaut-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <!-- Uncomment to enable incremental compilation -->
                    <!-- <useIncrementalCompilation>false</useIncrementalCompilation> -->
                    <annotationProcessorPaths combine.children="append">
                    </annotationProcessorPaths>
                    <compilerArgs>
                        <arg>-Amicronaut.processing.group=com.ashrithgn</arg>
                        <arg>-Amicronaut.processing.module=news</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>3.1.0</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

File Upload

When we upload the file from the rest client or JS library make sure the file upload  forma is Multipart Form Data. which has the header value `multipart/form-data`, The first pieces specifies that our request was submitted as multipart/form-data and the boundary is what is used to separate the "multiple parts" of the multipart request body.

Now we shall dive into java Side

Step 1 : Override default HTTP HttpServerConfiguration. This configuration will or helps to override default values as I am using Netty server i need to enable multipart request in MultipartConfiguration. Also there are other configuration you and override like max size of the payload.

@Singleton
@Replaces(HttpServerConfiguration.class)
public class MultiPartConfig extends HttpServerConfiguration {
    public MultiPartConfig() {
        super();
        MultipartConfiguration configuration = this.getMultipart();
        configuration.setEnabled(true);
        this.setMultipart(configuration);
    }


}

Step 2: writing the logic controller in controller to handle file upload and download.

@Controller
public class FileController {
    @Post(value = "/upload", consumes = {MediaType.MULTIPART_FORM_DATA})

    public boolean uploadFile(@Part CompletedFileUpload file) throws IOException {
        FileOutputStream fout = new FileOutputStream("../" + file.getFilename());
        fout.write(file.getBytes());
        fout.close();

        return true;
    }

    @Get(value = "/download", consumes = {MediaType.MULTIPART_FORM_DATA})
    public HttpResponse<byte[]> downLoadFile() throws IOException {
        return HttpResponse.ok(Files.readAllBytes(new File("<path>.jpg").toPath()))
                .header("Content-type", "application/octet-stream")
                .header("Content-disposition", "attachment; filename=\"name.jpg\"");
    }
}

In the Upload file method i am instructing my controller to consume data which is multipart/form-data, and i am reading the input using `@part` annotation. and file we receive is  Completed File type, this upload happen in non reactive way.

And in download File Method, i am send the files as bytes and setting the header `Content-type` as `application/octet-stream` or also you can set the header to know file type application/pdf, And also i am forcing to download by setting `Content-disposition` header as attachment.