or run

tessl search
Log in

Version

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
mavenpkg:maven/io.quarkus/quarkus-resteasy-reactive@3.15.x

docs

index.md
tile.json

tessl/maven-io-quarkus--quarkus-resteasy-reactive

tessl install tessl/maven-io-quarkus--quarkus-resteasy-reactive@3.15.0

A Jakarta REST implementation utilizing build time processing and Vert.x for high-performance REST endpoints with reactive capabilities in cloud-native environments.

multipart.mddocs/reference/

Multipart File Handling

Quarkus REST provides comprehensive support for multipart form data, enabling file uploads and downloads with easy access to file metadata and content.

Import

import org.jboss.resteasy.reactive.multipart.FileUpload;
import org.jboss.resteasy.reactive.multipart.FileDownload;
import org.jboss.resteasy.reactive.multipart.FilePart;
import org.jboss.resteasy.reactive.PartType;
import org.jboss.resteasy.reactive.PartFilename;
import org.jboss.resteasy.reactive.MultipartForm;
import org.jboss.resteasy.reactive.RestForm;
import jakarta.ws.rs.core.MediaType;
import io.smallrye.common.annotation.Blocking;

Interfaces

FilePart

Base interface for file parts in multipart requests and responses.

public interface FilePart {
    String name();          // Form field name
    Path filePath();        // File path (deprecated, use specific methods)
    String fileName();      // Original filename
    long size();            // File size in bytes
    String contentType();   // Content type
}

FileUpload

Represents an uploaded file in a multipart request (server-side).

public interface FileUpload extends FilePart {
    String ALL = "*";       // Constant to request all uploaded files

    Path uploadedFile();    // Path to the uploaded file

    // Inherited from FilePart
    String name();
    String fileName();
    long size();
    String contentType();
    String charSet();
}

FileDownload

Represents a file to be downloaded in a multipart response (server-side).

public interface FileDownload extends FilePart {
    Path filePath();        // Path to the file to download

    // Inherited from FilePart
    String name();
    String fileName();
    long size();
    String contentType();
    String charSet();
}

Annotations

@PartType

Specifies the media type for a multipart form field.

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PartType {
    String value();  // Media type
}

@PartFilename

Sets the filename for a multipart file upload (client-side).

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface PartFilename {
    String value();  // Filename
}

@MultipartForm (Deprecated)

Marks a parameter as a multipart form. This annotation is deprecated and no longer needed.

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Deprecated
public @interface MultipartForm {
}

File Upload Patterns

Simple File Upload

@POST
@Path("/upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Blocking
public Response uploadFile(@RestForm FileUpload file) throws IOException {
    // Access file metadata
    String filename = file.fileName();
    String contentType = file.contentType();
    long size = file.size();

    // Read file content
    byte[] content = Files.readAllBytes(file.uploadedFile());

    // Process file
    fileService.save(filename, content);

    return Response.ok().build();
}

Multiple Files Upload

@POST
@Path("/upload/multiple")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Blocking
public Response uploadMultipleFiles(@RestForm List<FileUpload> files) throws IOException {
    for (FileUpload file : files) {
        byte[] content = Files.readAllBytes(file.uploadedFile());
        fileService.save(file.fileName(), content);
    }
    return Response.ok().build();
}

// Or request all files
@POST
@Path("/upload/all")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Blocking
public Response uploadAllFiles(@RestForm(FileUpload.ALL) List<FileUpload> files) throws IOException {
    for (FileUpload file : files) {
        processFile(file);
    }
    return Response.ok().build();
}

Upload with Metadata

public class FileUploadForm {
    @RestForm("file")
    public FileUpload file;

    @RestForm
    public String description;

    @RestForm
    @PartType(MediaType.APPLICATION_JSON)
    public Metadata metadata;
}

@POST
@Path("/upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Blocking
public Response uploadWithMetadata(FileUploadForm form) throws IOException {
    // Access file
    byte[] content = Files.readAllBytes(form.file.uploadedFile());

    // Access metadata
    String description = form.description;
    Metadata metadata = form.metadata;

    fileService.save(form.file.fileName(), content, description, metadata);

    return Response.ok().build();
}

Multipart Form Bean

public class UploadRequest {
    @RestForm
    @PartType(MediaType.TEXT_PLAIN)
    public String title;

    @RestForm
    @PartType(MediaType.TEXT_PLAIN)
    public String description;

    @RestForm("document")
    public FileUpload document;

    @RestForm("thumbnail")
    public FileUpload thumbnail;

    @RestForm
    @PartType(MediaType.APPLICATION_JSON)
    public List<String> tags;
}

@POST
@Path("/documents")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Blocking
public Response uploadDocument(UploadRequest request) throws IOException {
    // Validate files
    if (request.document == null) {
        return Response.status(400).entity("Document required").build();
    }

    // Process main document
    byte[] docContent = Files.readAllBytes(request.document.uploadedFile());

    // Process optional thumbnail
    byte[] thumbContent = null;
    if (request.thumbnail != null) {
        thumbContent = Files.readAllBytes(request.thumbnail.uploadedFile());
    }

    // Save with metadata
    Document doc = documentService.create(
        request.title,
        request.description,
        docContent,
        thumbContent,
        request.tags
    );

    return Response.status(201)
        .entity(doc)
        .build();
}

File Download Patterns

Simple File Download

import org.jboss.resteasy.reactive.multipart.FileDownload;

@GET
@Path("/download/{id}")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response downloadFile(@RestPath String id) {
    FileData file = fileService.getFile(id);

    return Response.ok(file.getContent())
        .header("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"")
        .header("Content-Type", file.getContentType())
        .build();
}

// Or using FileDownload
@GET
@Path("/download/{id}/part")
@Produces(MediaType.MULTIPART_FORM_DATA)
public FileDownload downloadFilePart(@RestPath String id) {
    Path filePath = fileService.getFilePath(id);
    return FileDownload.of(filePath);
}

Streaming File Download

@GET
@Path("/download/{id}/stream")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public File downloadStream(@RestPath String id) {
    // Return File directly for streaming
    return fileService.getFile(id);
}

// Or with Path
@GET
@Path("/download/{id}/path")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Path downloadPath(@RestPath String id) {
    return fileService.getFilePath(id);
}

Reactive File Upload

@POST
@Path("/upload/async")
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Uni<Response> uploadAsync(@RestForm FileUpload file) {
    return Uni.createFrom().item(() -> {
        try {
            byte[] content = Files.readAllBytes(file.uploadedFile());
            return content;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    })
    .chain(content -> fileService.saveAsync(file.fileName(), content))
    .map(saved -> Response.ok().build());
}

File Validation

public class FileUploadForm {
    @RestForm("file")
    public FileUpload file;

    @RestForm
    public String description;
}

@POST
@Path("/upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Blocking
public Response uploadWithValidation(FileUploadForm form) {
    FileUpload file = form.file;

    // Validate file size
    if (file.size() > 10 * 1024 * 1024) { // 10MB
        return Response.status(413)
            .entity("File too large (max 10MB)")
            .build();
    }

    // Validate content type
    if (!file.contentType().startsWith("image/")) {
        return Response.status(400)
            .entity("Only image files allowed")
            .build();
    }

    // Validate filename
    String filename = file.fileName();
    if (!filename.matches("^[a-zA-Z0-9._-]+$")) {
        return Response.status(400)
            .entity("Invalid filename")
            .build();
    }

    try {
        byte[] content = Files.readAllBytes(file.uploadedFile());
        fileService.save(filename, content);
        return Response.ok().build();
    } catch (IOException e) {
        return Response.status(500)
            .entity("Upload failed")
            .build();
    }
}

Temporary File Handling

Uploaded files are stored as temporary files. They are automatically deleted after the request completes. To keep the file, copy it to a permanent location:

@POST
@Path("/upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Blocking
public Response upload(@RestForm FileUpload file) throws IOException {
    // Temporary file path
    Path tempFile = file.uploadedFile();

    // Copy to permanent location
    Path permanentLocation = Paths.get("/data/uploads/" + file.fileName());
    Files.copy(tempFile, permanentLocation, StandardCopyOption.REPLACE_EXISTING);

    return Response.ok().build();
}

Configuration

Configure multipart handling via application.properties:

# Default charset for multipart parts
quarkus.rest.multipart.input-part.default-charset=UTF-8

Blocking Consideration

File I/O operations are blocking. Use @Blocking annotation to execute on a worker thread:

import io.smallrye.common.annotation.Blocking;

@POST
@Path("/upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Blocking  // Execute on worker thread
public Response upload(@RestForm FileUpload file) throws IOException {
    byte[] content = Files.readAllBytes(file.uploadedFile());
    fileService.save(file.fileName(), content);
    return Response.ok().build();
}

Alternatively, use reactive approaches with Uni for non-blocking file operations.

Server-Side Multipart Classes

MultipartFormDataOutput

Class for constructing multipart responses on the server.

public class MultipartFormDataOutput {
    // Get all form data
    Map<String, List<PartItem>> getAllFormData();

    // Add form data parts
    PartItem addFormData(String key, Object entity, MediaType mediaType);
    PartItem addFormData(String key, Object entity, String genericType, MediaType mediaType);
    PartItem addFormData(String key, Object entity, MediaType mediaType, String filename);
}

Usage:

@GET
@Path("/download/multipart")
@Produces(MediaType.MULTIPART_FORM_DATA)
public MultipartFormDataOutput downloadMultipart() {
    MultipartFormDataOutput output = new MultipartFormDataOutput();

    // Add text part
    output.addFormData("description", "File package", MediaType.TEXT_PLAIN_TYPE);

    // Add JSON part
    Metadata metadata = new Metadata("file1", 12345);
    output.addFormData("metadata", metadata, MediaType.APPLICATION_JSON_TYPE);

    // Add file part
    File file = new File("/path/to/file.pdf");
    output.addFormData("file", file, MediaType.APPLICATION_OCTET_STREAM_TYPE, "document.pdf");

    return output;
}

MultipartFormDataInput

Interface for reading multipart request data on the server.

public interface MultipartFormDataInput {
    /**
     * Get all form values as a map
     * @return Map of field names to collections of form values
     */
    Map<String, Collection<FormValue>> getValues();
}

Usage:

@POST
@Path("/upload/advanced")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Blocking
public Response uploadAdvanced(MultipartFormDataInput input) throws IOException {
    Map<String, Collection<FormValue>> values = input.getValues();

    // Get text fields
    Collection<FormValue> descriptions = values.get("description");
    String description = descriptions.iterator().next().getValue();

    // Get file fields
    Collection<FormValue> files = values.get("file");
    for (FormValue fileValue : files) {
        if (fileValue.isFileItem()) {
            FileItem fileItem = fileValue.getFileItem();
            processFile(fileItem.getFile(), fileItem.getFileName());
        }
    }

    return Response.ok().build();
}

Exception Handling

MultipartPartReadingException

Exception thrown when multipart part reading fails.

public class MultipartPartReadingException extends RuntimeException {
    // Standard exception class for multipart errors
}

Handling:

@ServerExceptionMapper
public RestResponse<ErrorResponse> handleMultipartError(MultipartPartReadingException ex) {
    ErrorResponse error = new ErrorResponse();
    error.setMessage("Failed to read multipart data: " + ex.getMessage());
    return RestResponse.status(400, error);
}

@POST
@Path("/upload")
@Consumes(MediaType.MULTIPART_FORM_DATA)
@Blocking
public Response upload(@RestForm FileUpload file) {
    try {
        // Process upload
        return Response.ok().build();
    } catch (Exception e) {
        throw new MultipartPartReadingException("Upload failed", e);
    }
}