Route Arguments
How the files are received by your method is determined by the type of the arguments. Data can be received a chunk at a time or when an upload is completed.
If the route argument name can’t or shouldn’t match the name of the part in the request, simply add the @Part annotation to the argument and specify the name that is expected to be in the request. |
Chunk Data Types
PartData is the data type used to represent a chunk of data received in a multipart request. There are methods on the PartData interface to convert the data to a byte[]
, InputStream, or a ByteBuffer.
Data can only be retrieved from a PartData once. The underlying buffer will be released which causes further attempts to fail. |
Route arguments of type Publisher<PartData> will be treated as only intended to receive a single file and each chunk of the received file will be sent downstream. If the generic type is something other than PartData, conversion will be attempted using Micronaut’s conversion service. Conversions to String
and byte[]
are supported by default.
If requirements dictate you must have knowledge about the metadata of the file being received, a special class called StreamingFileUpload has been created that is a Publisher<PartData>, but also has file information like the content type and file name.
Streaming file upload
import io.micronaut.http.HttpResponse;
import io.micronaut.http.HttpStatus;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
import io.micronaut.http.multipart.StreamingFileUpload;
import io.reactivex.Single;
import java.io.File;
import org.reactivestreams.Publisher;
import java.io.IOException;
@Controller("/upload")
public class UploadController {
@Post(value = "/", consumes = MediaType.MULTIPART_FORM_DATA, produces = MediaType.TEXT_PLAIN) (1)
public Single<HttpResponse<String>> upload(StreamingFileUpload file) { (2)
File tempFile;
try {
tempFile = File.createTempFile(file.getFilename(), "temp");
} catch (IOException e) {
return Single.error(e);
}
Publisher<Boolean> uploadPublisher = file.transferTo(tempFile); (3)
return Single.fromPublisher(uploadPublisher) (4)
.map(success -> {
if (success) {
return HttpResponse.ok("Uploaded");
} else {
return HttpResponse.<String>status(HttpStatus.CONFLICT)
.body("Upload Failed");
}
});
}
}
Streaming file upload
import io.micronaut.http.HttpResponse
import io.micronaut.http.HttpStatus
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Post
import io.micronaut.http.multipart.StreamingFileUpload
import io.reactivex.Single
import org.reactivestreams.Publisher
@Controller("/upload")
class UploadController {
@Post(value = "/", consumes = MediaType.MULTIPART_FORM_DATA, produces = MediaType.TEXT_PLAIN) (1)
Single<HttpResponse<String>> upload(StreamingFileUpload file) { (2)
File tempFile = File.createTempFile(file.filename, "temp")
Publisher<Boolean> uploadPublisher = file.transferTo(tempFile) (3)
Single.fromPublisher(uploadPublisher) (4)
.map({ success ->
if (success) {
HttpResponse.ok("Uploaded")
} else {
HttpResponse.<String>status(HttpStatus.CONFLICT)
.body("Upload Failed")
}
})
}
}
Streaming file upload
import io.micronaut.http.HttpResponse
import io.micronaut.http.HttpStatus
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Post
import io.micronaut.http.multipart.StreamingFileUpload
import io.reactivex.Single
import java.io.File
@Controller("/upload")
class UploadController {
@Post(value = "/", consumes = [MediaType.MULTIPART_FORM_DATA], produces = [MediaType.TEXT_PLAIN]) (1)
fun upload(file: StreamingFileUpload): Single<HttpResponse<String>> { (2)
val tempFile = File.createTempFile(file.filename, "temp")
val uploadPublisher = file.transferTo(tempFile) (3)
return Single.fromPublisher(uploadPublisher) (4)
.map { success ->
if (success) {
HttpResponse.ok("Uploaded")
} else {
HttpResponse.status<String>(HttpStatus.CONFLICT)
.body("Upload Failed")
}
}
}
}
1 | The method is set to consume MULTIPART_FORM_DATA |
2 | The method parameters match form attribute names. In this case the file will match for example an <input type=”file” name=”file”> |
3 | The StreamingFileUpload.transferTo(java.lang.String) method is used to transfer the file to the server. The method returns a Publisher |
4 | The returned Single subscribes to the Publisher and outputs a response once the upload is complete, without blocking. |
Whole Data Types
Route arguments that are not publishers will cause the 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 has been 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
import io.micronaut.http.HttpResponse;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@Controller("/upload")
public class BytesUploadController {
@Post(value = "/bytes",
consumes = MediaType.MULTIPART_FORM_DATA,
produces = MediaType.TEXT_PLAIN) (1)
public HttpResponse<String> uploadBytes(byte[] file, String fileName) { (2)
try {
File tempFile = File.createTempFile(fileName, "temp");
Path path = Paths.get(tempFile.getAbsolutePath());
Files.write(path, file); (3)
return HttpResponse.ok("Uploaded");
} catch (IOException exception) {
return HttpResponse.badRequest("Upload Failed");
}
}
}
Receiving a byte array
import io.micronaut.http.HttpResponse
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Post
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
@Controller("/upload")
class BytesUploadController {
@Post(value = "/bytes",
consumes = MediaType.MULTIPART_FORM_DATA,
produces = MediaType.TEXT_PLAIN) (1)
HttpResponse<String> uploadBytes(byte[] file, String fileName) { (2)
try {
File tempFile = File.createTempFile(fileName, "temp")
Path path = Paths.get(tempFile.absolutePath)
Files.write(path, file) (3)
HttpResponse.ok("Uploaded")
} catch (IOException exception) {
HttpResponse.badRequest("Upload Failed")
}
}
}
Receiving a byte array
import io.micronaut.http.HttpResponse
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Post
import java.io.File
import java.io.IOException
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
@Controller("/upload")
class BytesUploadController {
@Post(value = "/bytes",
consumes = [MediaType.MULTIPART_FORM_DATA],
produces = [MediaType.TEXT_PLAIN]) (1)
fun uploadBytes(file: ByteArray, fileName: String): HttpResponse<String> { (2)
return try {
val tempFile = File.createTempFile(fileName, "temp")
val path = Paths.get(tempFile.absolutePath)
Files.write(path, file) (3)
HttpResponse.ok("Uploaded")
} catch (exception: IOException) {
HttpResponse.badRequest("Upload Failed")
}
}
}
If requirements dictate you must have knowledge about the metadata of the file being received, a special class called CompletedFileUpload has been created that has methods to retrieve the data of the file, but also has file information like the content type and file name.
File upload with metadata
import io.micronaut.http.HttpResponse;
import io.micronaut.http.MediaType;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Post;
import io.micronaut.http.multipart.CompletedFileUpload;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@Controller("/upload")
public class CompletedUploadController {
@Post(value = "/completed",
consumes = MediaType.MULTIPART_FORM_DATA,
produces = MediaType.TEXT_PLAIN) (1)
public HttpResponse<String> uploadCompleted(CompletedFileUpload file) { (2)
try {
File tempFile = File.createTempFile(file.getFilename(), "temp"); (3)
Path path = Paths.get(tempFile.getAbsolutePath());
Files.write(path, file.getBytes()); (3)
return HttpResponse.ok("Uploaded");
} catch (IOException exception) {
return HttpResponse.badRequest("Upload Failed");
}
}
}
File upload with metadata
import io.micronaut.http.HttpResponse
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Post
import io.micronaut.http.multipart.CompletedFileUpload
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
@Controller("/upload")
class CompletedUploadController {
@Post(value = "/completed",
consumes = MediaType.MULTIPART_FORM_DATA,
produces = MediaType.TEXT_PLAIN) (1)
HttpResponse<String> uploadCompleted(CompletedFileUpload file) { (2)
try {
File tempFile = File.createTempFile(file.filename, "temp") (3)
Path path = Paths.get(tempFile.absolutePath)
Files.write(path, file.bytes) (3)
HttpResponse.ok("Uploaded")
} catch (IOException exception) {
HttpResponse.badRequest("Upload Failed")
}
}
}
File upload with metadata
import io.micronaut.http.HttpResponse
import io.micronaut.http.MediaType
import io.micronaut.http.annotation.Controller
import io.micronaut.http.annotation.Post
import io.micronaut.http.multipart.CompletedFileUpload
import java.io.File
import java.io.IOException
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.Paths
@Controller("/upload")
class CompletedUploadController {
@Post(value = "/completed",
consumes = [MediaType.MULTIPART_FORM_DATA],
produces = [MediaType.TEXT_PLAIN]) (1)
fun uploadCompleted(file: CompletedFileUpload): HttpResponse<String> { (2)
return try {
val tempFile = File.createTempFile(file.filename, "temp") (3)
val path = Paths.get(tempFile.absolutePath)
Files.write(path, file.bytes) (3)
HttpResponse.ok("Uploaded")
} catch (exception: IOException) {
HttpResponse.badRequest("Upload Failed")
}
}
}
1 | The method is set to consume MULTIPART_FORM_DATA |
2 | The method parameters match form attribute names. In this case the file will match for example an <input type=”file” name=”file”> |
3 | The CompletedFileUpload instance gives access to metadata about the upload as well as access to the file’s contents. |