Uploading Files

MultipartFile

Browsers and apps send files as multipart/form-data. Spring gives each file to your controller as a MultipartFile.

@RestController
@RequestMapping("/api/products/{id}/images")
@RequiredArgsConstructor
public class ProductImageController {
    private final ImageStorageService storage;

    @PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public ResponseEntity<ImageResponse> upload(@PathVariable Long id,
                                                @RequestParam("file") MultipartFile file) {
        ImageResponse saved = storage.store(id, file);
        return ResponseEntity.created(URI.create(saved.url())).body(saved);
    }

    @PostMapping(path = "/bulk", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
    public List<ImageResponse> uploadMany(@PathVariable Long id,
                                          @RequestParam("files") List<MultipartFile> files) {
        return files.stream().map(f -> storage.store(id, f)).toList();
    }
}

Size Limits

Example

The defaults are 1 MB per file and 10 MB per request. Set your own:

application.yml

spring:
  servlet:
    multipart:
      max-file-size: 5MB
      max-request-size: 20MB

Example

Upload with curl (-F sends multipart form data):

curl -i -F "[email protected]" localhost:8080/api/products/3/images

Example output

HTTP/1.1 201 Created
Location: /api/files/products/3/9b2f4c1e.jpg
Content-Type: application/json

{"fileName":"9b2f4c1e.jpg","originalName":"pixel9-front.jpg","size":348211,"contentType":"image/jpeg","url":"/api/files/products/3/9b2f4c1e.jpg"}

Note: A too-large file throws MaxUploadSizeExceededException. Handle it in your @RestControllerAdvice and return 413 Payload Too Large.

Uploading from React

async function uploadImage(productId, file) {
  const form = new FormData();
  form.append('file', file);                     // do NOT set Content-Type yourself
  const res = await fetch(`/api/products/${productId}/images`, { method: 'POST', body: form });
  if (!res.ok) throw new Error('Upload failed: ' + res.status);
  return res.json();
}

<input type="file" accept="image/png,image/jpeg,image/webp"
       onChange={e => uploadImage(3, e.target.files[0])} />