or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

api-versioning.mdasync.mdconfiguration.mdcontent-negotiation.mdcontroller-annotations.mdcore-framework.mdcors.mddata-binding.mdexception-handling.mdflash-attributes.mdfunctional-endpoints.mdi18n.mdindex.mdinterceptors.mdmultipart.mdrequest-binding.mdresource-handling.mdresponse-handling.mduri-building.mdview-resolution.md
tile.json

multipart.mddocs/

Multipart File Upload

Support for handling multipart file uploads in forms and API endpoints with automatic binding to method parameters, including MultipartFile interface and related components.

Capabilities

MultipartFile

Represents an uploaded file in a multipart request.

/**
 * A representation of an uploaded file received in a multipart request.
 *
 * The file contents are either stored in memory or temporarily on disk.
 * In either case, the user is responsible for copying file contents to a
 * session-level or persistent store as and if desired. The temporary storage
 * will be cleared at the end of request processing.
 */
public interface MultipartFile {
    /**
     * Return the name of the parameter in the multipart form.
     *
     * @return the name of the parameter
     */
    String getName();

    /**
     * Return the original filename in the client's filesystem.
     * This may contain path information depending on the browser used,
     * but it typically will not with any other than Opera.
     *
     * @return the original filename, or the empty String if no file was chosen
     */
    String getOriginalFilename();

    /**
     * Return the content type of the file.
     *
     * @return the content type, or null if not defined
     */
    String getContentType();

    /**
     * Return whether the uploaded file is empty, that is, either no file has
     * been chosen in the multipart form or the chosen file has no content.
     *
     * @return whether the uploaded file is empty
     */
    boolean isEmpty();

    /**
     * Return the size of the file in bytes.
     *
     * @return the size of the file, or 0 if empty
     */
    long getSize();

    /**
     * Return the contents of the file as an array of bytes.
     *
     * @return the contents of the file as bytes, or an empty byte array if empty
     * @throws IOException in case of access errors
     */
    byte[] getBytes() throws IOException;

    /**
     * Return an InputStream to read the contents of the file from.
     *
     * @return the contents of the file as stream, or an empty stream if empty
     * @throws IOException in case of access errors
     */
    InputStream getInputStream() throws IOException;

    /**
     * Transfer the received file to the given destination file.
     *
     * @param dest the destination file
     * @throws IOException in case of reading or writing errors
     * @throws IllegalStateException if the file has already been moved
     */
    void transferTo(File dest) throws IOException, IllegalStateException;

    /**
     * Transfer the received file to the given destination file.
     *
     * @param dest the destination file
     * @throws IOException in case of reading or writing errors
     * @throws IllegalStateException if the file has already been moved
     */
    default void transferTo(Path dest) throws IOException, IllegalStateException {
        transferTo(dest.toFile());
    }
}

Usage Example:

@RestController
@RequestMapping("/api/upload")
public class FileUploadController {

    @PostMapping("/single")
    public ResponseEntity<FileInfo> uploadFile(@RequestParam("file") MultipartFile file) {
        if (file.isEmpty()) {
            return ResponseEntity.badRequest().build();
        }

        try {
            String filename = file.getOriginalFilename();
            long size = file.getSize();
            String contentType = file.getContentType();

            // Save file
            Path path = Paths.get(uploadDir, filename);
            file.transferTo(path);

            FileInfo info = new FileInfo(filename, size, contentType);
            return ResponseEntity.ok(info);
        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

    @PostMapping("/multiple")
    public ResponseEntity<List<FileInfo>> uploadMultipleFiles(
            @RequestParam("files") List<MultipartFile> files) {
        List<FileInfo> uploadedFiles = new ArrayList<>();

        for (MultipartFile file : files) {
            if (!file.isEmpty()) {
                try {
                    Path path = Paths.get(uploadDir, file.getOriginalFilename());
                    file.transferTo(path);

                    uploadedFiles.add(new FileInfo(
                        file.getOriginalFilename(),
                        file.getSize(),
                        file.getContentType()
                    ));
                } catch (IOException e) {
                    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
                }
            }
        }

        return ResponseEntity.ok(uploadedFiles);
    }

    @PostMapping("/with-data")
    public ResponseEntity<Document> uploadWithMetadata(
            @RequestParam("file") MultipartFile file,
            @RequestParam("title") String title,
            @RequestParam("description") String description) {
        try {
            byte[] content = file.getBytes();
            Document doc = documentService.save(title, description, content, file.getContentType());
            return ResponseEntity.status(HttpStatus.CREATED).body(doc);
        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }
}

MultipartResolver

Strategy interface for multipart file upload resolution.

/**
 * A strategy interface for multipart file upload resolution in accordance
 * with RFC 1867. Implementations are typically usable both within an
 * application context and standalone.
 */
public interface MultipartResolver {
    /**
     * Determine if the given request contains multipart content.
     *
     * @param request the servlet request to be evaluated
     * @return whether the request contains multipart content
     */
    boolean isMultipart(HttpServletRequest request);

    /**
     * Parse the given HTTP request into multipart files and parameters,
     * and wrap the request inside a MultipartHttpServletRequest object
     * that provides access to file descriptors and makes contained
     * parameters accessible via the standard ServletRequest methods.
     *
     * @param request the servlet request to wrap
     * @return the wrapped servlet request
     * @throws MultipartException if the servlet request is not multipart
     */
    MultipartHttpServletRequest resolveMultipart(HttpServletRequest request)
            throws MultipartException;

    /**
     * Cleanup any resources used for the multipart handling,
     * like a storage for the uploaded files.
     *
     * @param request the request to cleanup resources for
     */
    void cleanupMultipart(MultipartHttpServletRequest request);
}

@RequestPart

Annotation for binding a part in a multipart request to a method parameter.

/**
 * Annotation that can be used to associate the part of a "multipart/form-data" request
 * with a method argument.
 *
 * Supported method argument types include MultipartFile in conjunction with Spring's
 * MultipartResolver abstraction, javax.servlet.http.Part in conjunction with Servlet 3.0
 * multipart requests, or otherwise for any other method argument, the content of the part
 * is passed through an HttpMessageConverter considering the 'Content-Type' header of the
 * request part.
 */
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestPart {
    @AliasFor("name")
    String value() default "";

    @AliasFor("value")
    String name() default "";

    boolean required() default true;
}

Usage Example:

@RestController
@RequestMapping("/api/documents")
public class DocumentController {

    @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseEntity<Document> createDocument(
            @RequestPart("file") MultipartFile file,
            @RequestPart("metadata") DocumentMetadata metadata) {
        try {
            Document doc = documentService.create(file, metadata);
            return ResponseEntity.status(HttpStatus.CREATED).body(doc);
        } catch (IOException e) {
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
    }

    @PostMapping("/gallery")
    public ResponseEntity<Gallery> createGallery(
            @RequestPart("images") List<MultipartFile> images,
            @RequestPart("info") GalleryInfo info) {
        Gallery gallery = galleryService.create(images, info);
        return ResponseEntity.status(HttpStatus.CREATED).body(gallery);
    }

    @PostMapping("/profile")
    public ResponseEntity<Profile> updateProfile(
            @RequestPart("profile") ProfileData profileData,
            @RequestPart(value = "avatar", required = false) MultipartFile avatar) {
        Profile profile = profileService.update(profileData, avatar);
        return ResponseEntity.ok(profile);
    }
}

Types

MultipartHttpServletRequest

Interface for multipart requests with access to file parts.

public interface MultipartHttpServletRequest extends HttpServletRequest {
    Iterator<String> getFileNames();
    MultipartFile getFile(String name);
    List<MultipartFile> getFiles(String name);
    Map<String, MultipartFile> getFileMap();
    MultiValueMap<String, MultipartFile> getMultiFileMap();
}

MultipartException

Exception thrown when multipart resolution fails.

public class MultipartException extends RuntimeException {
    public MultipartException(String msg) {}
    public MultipartException(String msg, Throwable cause) {}
}

Configuration

@Configuration
public class MultipartConfig {

    @Bean
    public MultipartResolver multipartResolver() {
        CommonsMultipartResolver resolver = new CommonsMultipartResolver();
        resolver.setMaxUploadSize(10485760); // 10MB
        resolver.setMaxUploadSizePerFile(5242880); // 5MB
        resolver.setMaxInMemorySize(1048576); // 1MB
        return resolver;
    }
}