Route Arguments

Method argument types determine how files are received. Data can be received a chunk at a time or when an upload is completed.

If the route argument name cannot or should not match the name of the part in the request, add the @Part annotation to the argument and specify the expected name in the request.

Chunk Data Types

PartData represents a chunk of data received in a multipart request. PartData interface methods convert the data to a byte[], InputStream, or a ByteBuffer.

Data can only be retrieved from a PartData once. The underlying buffer is released, causing further attempts to fail.

Route arguments of type Publisher<PartData> are treated as intended to receive a single file, and each chunk of the received file will be sent downstream. If the generic type is other than PartData, conversion will be attempted using Micronaut’s conversion service. Conversions to String and byte[] are supported by default.

If you need knowledge about the metadata of an uploaded file, the StreamingFileUpload class is a Publisher<PartData> that also has file information such as the content type and file name.

Streaming file upload

  1. import io.micronaut.http.HttpResponse;
  2. import io.micronaut.http.annotation.Controller;
  3. import io.micronaut.http.annotation.Post;
  4. import io.micronaut.http.multipart.StreamingFileUpload;
  5. import org.reactivestreams.Publisher;
  6. import reactor.core.publisher.Mono;
  7. import io.micronaut.core.async.annotation.SingleResult;
  8. import java.io.File;
  9. import java.io.IOException;
  10. import static io.micronaut.http.HttpStatus.CONFLICT;
  11. import static io.micronaut.http.MediaType.MULTIPART_FORM_DATA;
  12. import static io.micronaut.http.MediaType.TEXT_PLAIN;
  13. @Controller("/upload")
  14. public class UploadController {
  15. @Post(value = "/", consumes = MULTIPART_FORM_DATA, produces = TEXT_PLAIN) (1)
  16. @SingleResult
  17. public Publisher<HttpResponse<String>> upload(StreamingFileUpload file) { (2)
  18. File tempFile;
  19. try {
  20. tempFile = File.createTempFile(file.getFilename(), "temp");
  21. } catch (IOException e) {
  22. return Mono.error(e);
  23. }
  24. Publisher<Boolean> uploadPublisher = file.transferTo(tempFile); (3)
  25. return Mono.from(uploadPublisher) (4)
  26. .map(success -> {
  27. if (success) {
  28. return HttpResponse.ok("Uploaded");
  29. } else {
  30. return HttpResponse.<String>status(CONFLICT)
  31. .body("Upload Failed");
  32. }
  33. });
  34. }
  35. }

Streaming file upload

  1. import io.micronaut.http.HttpResponse
  2. import io.micronaut.http.annotation.Controller
  3. import io.micronaut.http.annotation.Post
  4. import io.micronaut.http.multipart.StreamingFileUpload
  5. import org.reactivestreams.Publisher
  6. import reactor.core.publisher.Mono
  7. import static io.micronaut.http.HttpStatus.CONFLICT
  8. import static io.micronaut.http.MediaType.MULTIPART_FORM_DATA
  9. import static io.micronaut.http.MediaType.TEXT_PLAIN
  10. @Controller("/upload")
  11. class UploadController {
  12. @Post(value = "/", consumes = MULTIPART_FORM_DATA, produces = TEXT_PLAIN) (1)
  13. Mono<HttpResponse<String>> upload(StreamingFileUpload file) { (2)
  14. File tempFile = File.createTempFile(file.filename, "temp")
  15. Publisher<Boolean> uploadPublisher = file.transferTo(tempFile) (3)
  16. Mono.from(uploadPublisher) (4)
  17. .map({ success ->
  18. if (success) {
  19. HttpResponse.ok("Uploaded")
  20. } else {
  21. HttpResponse.<String>status(CONFLICT)
  22. .body("Upload Failed")
  23. }
  24. })
  25. }
  26. }

Streaming file upload

  1. import io.micronaut.http.HttpResponse
  2. import io.micronaut.http.HttpStatus.CONFLICT
  3. import io.micronaut.http.MediaType.MULTIPART_FORM_DATA
  4. import io.micronaut.http.MediaType.TEXT_PLAIN
  5. import io.micronaut.http.annotation.Controller
  6. import io.micronaut.http.annotation.Post
  7. import io.micronaut.http.multipart.StreamingFileUpload
  8. import reactor.core.publisher.Mono
  9. import java.io.File
  10. @Controller("/upload")
  11. class UploadController {
  12. @Post(value = "/", consumes = [MULTIPART_FORM_DATA], produces = [TEXT_PLAIN]) (1)
  13. fun upload(file: StreamingFileUpload): Mono<HttpResponse<String>> { (2)
  14. val tempFile = File.createTempFile(file.filename, "temp")
  15. val uploadPublisher = file.transferTo(tempFile) (3)
  16. return Mono.from(uploadPublisher) (4)
  17. .map { success ->
  18. if (success) {
  19. HttpResponse.ok("Uploaded")
  20. } else {
  21. HttpResponse.status<String>(CONFLICT)
  22. .body("Upload Failed")
  23. }
  24. }
  25. }
  26. }
1The method consumes MULTIPART_FORM_DATA
2The method parameters match form attribute names. In this case file will match for example an <input type=”file” name=”file”>
3The StreamingFileUpload.transferTo(java.lang.String) method transfers the file to the server. The method returns a Publisher
4The returned reactor:Mono[] subscribes to the Publisher and outputs a response once the upload is complete, without blocking.

Whole Data Types

Route arguments that are not publishers cause route execution to be delayed until the upload has finished. The received data will attempt to be converted to the requested type. Conversions to a String or byte[] are supported by default. In addition, the file can be converted to a POJO if a media type codec is registered that supports the media type of the file. A media type codec is included by default that allows conversion of JSON files to POJOs.

Receiving a byte array

  1. import io.micronaut.http.HttpResponse;
  2. import io.micronaut.http.annotation.Controller;
  3. import io.micronaut.http.annotation.Post;
  4. import java.io.File;
  5. import java.io.IOException;
  6. import java.nio.file.Files;
  7. import java.nio.file.Path;
  8. import java.nio.file.Paths;
  9. import static io.micronaut.http.MediaType.MULTIPART_FORM_DATA;
  10. import static io.micronaut.http.MediaType.TEXT_PLAIN;
  11. @Controller("/upload")
  12. public class BytesUploadController {
  13. @Post(value = "/bytes", consumes = MULTIPART_FORM_DATA, produces = TEXT_PLAIN) (1)
  14. public HttpResponse<String> uploadBytes(byte[] file, String fileName) { (2)
  15. try {
  16. File tempFile = File.createTempFile(fileName, "temp");
  17. Path path = Paths.get(tempFile.getAbsolutePath());
  18. Files.write(path, file); (3)
  19. return HttpResponse.ok("Uploaded");
  20. } catch (IOException e) {
  21. return HttpResponse.badRequest("Upload Failed");
  22. }
  23. }
  24. }

Receiving a byte array

  1. import io.micronaut.http.HttpResponse
  2. import io.micronaut.http.annotation.Controller
  3. import io.micronaut.http.annotation.Post
  4. import java.nio.file.Files
  5. import java.nio.file.Path
  6. import java.nio.file.Paths
  7. import static io.micronaut.http.MediaType.MULTIPART_FORM_DATA
  8. import static io.micronaut.http.MediaType.TEXT_PLAIN
  9. @Controller("/upload")
  10. class BytesUploadController {
  11. @Post(value = "/bytes", consumes = MULTIPART_FORM_DATA, produces = TEXT_PLAIN) (1)
  12. HttpResponse<String> uploadBytes(byte[] file, String fileName) { (2)
  13. try {
  14. File tempFile = File.createTempFile(fileName, "temp")
  15. Path path = Paths.get(tempFile.absolutePath)
  16. Files.write(path, file) (3)
  17. HttpResponse.ok("Uploaded")
  18. } catch (IOException e) {
  19. HttpResponse.badRequest("Upload Failed")
  20. }
  21. }
  22. }

Receiving a byte array

  1. import io.micronaut.http.HttpResponse
  2. import io.micronaut.http.MediaType
  3. import io.micronaut.http.MediaType.MULTIPART_FORM_DATA
  4. import io.micronaut.http.MediaType.TEXT_PLAIN
  5. import io.micronaut.http.annotation.Controller
  6. import io.micronaut.http.annotation.Post
  7. import java.io.File
  8. import java.io.IOException
  9. import java.nio.file.Files
  10. import java.nio.file.Paths
  11. @Controller("/upload")
  12. class BytesUploadController {
  13. @Post(value = "/bytes", consumes = [MULTIPART_FORM_DATA], produces = [TEXT_PLAIN]) (1)
  14. fun uploadBytes(file: ByteArray, fileName: String): HttpResponse<String> { (2)
  15. return try {
  16. val tempFile = File.createTempFile(fileName, "temp")
  17. val path = Paths.get(tempFile.absolutePath)
  18. Files.write(path, file) (3)
  19. HttpResponse.ok("Uploaded")
  20. } catch (e: IOException) {
  21. HttpResponse.badRequest("Upload Failed")
  22. }
  23. }
  24. }

If you need knowledge about the metadata of an uploaded file, the CompletedFileUpload class has methods to retrieve the data of the file, and also file information such as the content type and file name.

File upload with metadata

  1. import io.micronaut.http.HttpResponse;
  2. import io.micronaut.http.annotation.Controller;
  3. import io.micronaut.http.annotation.Post;
  4. import io.micronaut.http.multipart.CompletedFileUpload;
  5. import java.io.File;
  6. import java.io.IOException;
  7. import java.nio.file.Files;
  8. import java.nio.file.Path;
  9. import java.nio.file.Paths;
  10. import static io.micronaut.http.MediaType.MULTIPART_FORM_DATA;
  11. import static io.micronaut.http.MediaType.TEXT_PLAIN;
  12. @Controller("/upload")
  13. public class CompletedUploadController {
  14. @Post(value = "/completed", consumes = MULTIPART_FORM_DATA, produces = TEXT_PLAIN) (1)
  15. public HttpResponse<String> uploadCompleted(CompletedFileUpload file) { (2)
  16. try {
  17. File tempFile = File.createTempFile(file.getFilename(), "temp"); (3)
  18. Path path = Paths.get(tempFile.getAbsolutePath());
  19. Files.write(path, file.getBytes()); (3)
  20. return HttpResponse.ok("Uploaded");
  21. } catch (IOException e) {
  22. return HttpResponse.badRequest("Upload Failed");
  23. }
  24. }
  25. }

File upload with metadata

  1. import io.micronaut.http.HttpResponse
  2. import io.micronaut.http.annotation.Controller
  3. import io.micronaut.http.annotation.Post
  4. import io.micronaut.http.multipart.CompletedFileUpload
  5. import java.nio.file.Files
  6. import java.nio.file.Path
  7. import java.nio.file.Paths
  8. import static io.micronaut.http.MediaType.MULTIPART_FORM_DATA
  9. import static io.micronaut.http.MediaType.TEXT_PLAIN
  10. @Controller("/upload")
  11. class CompletedUploadController {
  12. @Post(value = "/completed", consumes = MULTIPART_FORM_DATA, produces = TEXT_PLAIN) (1)
  13. HttpResponse<String> uploadCompleted(CompletedFileUpload file) { (2)
  14. try {
  15. File tempFile = File.createTempFile(file.filename, "temp") (3)
  16. Path path = Paths.get(tempFile.absolutePath)
  17. Files.write(path, file.bytes) (3)
  18. HttpResponse.ok("Uploaded")
  19. } catch (IOException e) {
  20. HttpResponse.badRequest("Upload Failed")
  21. }
  22. }
  23. }

File upload with metadata

  1. import io.micronaut.http.HttpResponse
  2. import io.micronaut.http.MediaType.MULTIPART_FORM_DATA
  3. import io.micronaut.http.MediaType.TEXT_PLAIN
  4. import io.micronaut.http.annotation.Controller
  5. import io.micronaut.http.annotation.Post
  6. import io.micronaut.http.multipart.CompletedFileUpload
  7. import java.io.File
  8. import java.io.IOException
  9. import java.nio.file.Files
  10. import java.nio.file.Paths
  11. @Controller("/upload")
  12. class CompletedUploadController {
  13. @Post(value = "/completed", consumes = [MULTIPART_FORM_DATA], produces = [TEXT_PLAIN]) (1)
  14. fun uploadCompleted(file: CompletedFileUpload): HttpResponse<String> { (2)
  15. return try {
  16. val tempFile = File.createTempFile(file.filename, "temp") (3)
  17. val path = Paths.get(tempFile.absolutePath)
  18. Files.write(path, file.bytes) (3)
  19. HttpResponse.ok("Uploaded")
  20. } catch (e: IOException) {
  21. HttpResponse.badRequest("Upload Failed")
  22. }
  23. }
  24. }
1The method consumes MULTIPART_FORM_DATA
2The method parameters match form attribute names. In this case the file will match for example an <input type=”file” name=”file”>
3The CompletedFileUpload instance gives access to metadata about the upload as well as access to the file contents.
If a file will not be read, the discard method on the file object must be called to prevent memory leaks.