How to handle multiple file uploads and downloads in Spring Boot REST endpoints

I’m working on a Spring Boot application where I need to create REST endpoints that can handle multiple files at once. For the upload endpoint, users should be able to send several files in one request. For the download endpoint, I need to return multiple files back to the client.

Here’s what I’m trying to achieve:

@PostMapping("/batch-upload")
public ResponseEntity<String> handleMultipleFiles(@RequestParam("documents") MultipartFile[] documents) {
    // Process multiple uploaded files
    return ResponseEntity.ok("Files processed");
}

@GetMapping("/batch-download")
public ResponseEntity<List<Resource>> getMultipleFiles(@RequestParam String requestId) {
    // Return multiple files to client
    return ResponseEntity.ok(fileList);
}

For each file I’m working with, I need access to the original filename, the content type, and the actual byte data. Creating a zip archive isn’t an option for my use case since the client application needs to process each file individually.

I’m primarily using Spring Boot but I’m open to integrating other libraries if needed. Has anyone implemented similar functionality before? What’s the recommended approach for this kind of multi-file handling?

I hit this same issue last year and found a different approach that might help. Skip trying to handle multiple downloads in one response - use streaming with multipart/mixed content instead. Your upload approach is right, but add file size validation and async processing for better performance:

@PostMapping("/batch-upload")
public ResponseEntity<String> handleMultipleFiles(@RequestParam("documents") MultipartFile[] documents) {
    CompletableFuture.runAsync(() -> {
        Arrays.stream(documents)
            .filter(file -> !file.isEmpty() && file.getSize() < MAX_FILE_SIZE)
            .forEach(this::processFile);
    });
    
    return ResponseEntity.accepted().body("Processing started");
}

For downloads, I stream multiple files as separate parts in a multipart response:

@GetMapping("/batch-download")
public void getMultipleFiles(@RequestParam String requestId, HttpServletResponse response) throws IOException {
    response.setContentType("multipart/mixed; boundary=----boundary");
    
    List<FileEntity> files = fileService.getFilesByRequestId(requestId);
    
    try (ServletOutputStream outputStream = response.getOutputStream()) {
        for (FileEntity file : files) {
            outputStream.println("------boundary");
            outputStream.println("Content-Type: " + file.getContentType());
            outputStream.println("Content-Disposition: attachment; filename=\"" + file.getOriginalName() + "\"");
            outputStream.println();
            outputStream.write(file.getData());
            outputStream.println();
        }
        outputStream.println("------boundary--");
    }
}

The client gets all files in one response but can process them individually by parsing the multipart content. Really useful when you need to keep file metadata and avoid multiple HTTP requests.

Been there many times. Your upload endpoint looks solid, but downloads need a different approach - you can’t return multiple files in one HTTP response.

Here’s what works:

Uploads - your current approach is fine, just add validation:

@PostMapping("/batch-upload")
public ResponseEntity<Map<String, String>> handleMultipleFiles(@RequestParam("documents") MultipartFile[] documents) {
    Map<String, String> results = new HashMap<>();
    
    for (MultipartFile file : documents) {
        if (!file.isEmpty()) {
            String filename = file.getOriginalFilename();
            String contentType = file.getContentType();
            // Store file and track results
            results.put(filename, "success");
        }
    }
    return ResponseEntity.ok(results);
}

Downloads - two solid options:

  1. Return file metadata with individual download links:
@GetMapping("/batch-download")
public ResponseEntity<List<FileInfo>> getFileList(@RequestParam String requestId) {
    List<FileInfo> files = fileService.getFilesByRequestId(requestId);
    return ResponseEntity.ok(files);
}

@GetMapping("/download/{fileId}")
public ResponseEntity<Resource> downloadSingleFile(@PathVariable String fileId) {
    // Return individual file
}
  1. Use multipart response (trickier but doable):
@GetMapping("/batch-download")
public void downloadMultipleFiles(@RequestParam String requestId, HttpServletResponse response) {
    response.setContentType("multipart/x-mixed-replace; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW");
    // Write each file as separate part
}

I usually go with option 1 - it’s cleaner and gives the client more control.

I’ve built similar multi-file endpoints in production, and your upload approach looks solid. The main challenge is downloads since HTTP responses can only contain one body.

Your upload code’s on the right track. I usually add some validation and processing:

@PostMapping("/batch-upload")
public ResponseEntity<Map<String, String>> handleMultipleFiles(@RequestParam("documents") MultipartFile[] documents) {
    Map<String, String> results = new HashMap<>();
    
    for (MultipartFile file : documents) {
        if (!file.isEmpty()) {
            String originalName = file.getOriginalFilename();
            String contentType = file.getContentType();
            byte[] bytes = file.getBytes();
            
            // Your processing logic here
            String fileId = saveFile(originalName, contentType, bytes);
            results.put(originalName, fileId);
        }
    }
    
    return ResponseEntity.ok(results);
}

For downloads, you can’t return multiple files in one HTTP response. I usually go with:

  1. Return metadata with individual download URLs for each file.
  2. Use multipart response format (like email attachments).

The metadata approach works better:

@GetMapping("/batch-download/{requestId}")
public ResponseEntity<List<FileMetadata>> getFilesList(@PathVariable String requestId) {
    List<FileMetadata> files = fileService.getFilesByRequestId(requestId);
    return ResponseEntity.ok(files);
}

@GetMapping("/download/{fileId}")
public ResponseEntity<Resource> downloadSingleFile(@PathVariable String fileId) {
    FileData fileData = fileService.getFile(fileId);
    ByteArrayResource resource = new ByteArrayResource(fileData.getContent());
    
    return ResponseEntity.ok()
        .contentType(MediaType.parseMediaType(fileData.getContentType()))
        .header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + fileData.getOriginalName() + "\"")
        .body(resource);
}

This video covers the basics really well:

The client can make parallel requests to download individual files, which actually performs better than streaming everything in one response.

I’ve dealt with this exact scenario in a document management system. The trick is handling downloads properly without overcomplicating things.

For uploads, your MultipartFile array approach works well. Store each file with a unique identifier and track the relationship to the original request:

@PostMapping("/batch-upload")
public ResponseEntity<BatchUploadResponse> handleMultipleFiles(@RequestParam("documents") MultipartFile[] documents) {
    String batchId = UUID.randomUUID().toString();
    List<String> fileIds = new ArrayList<>();
    
    for (MultipartFile file : documents) {
        String fileId = fileStorageService.store(file, batchId);
        fileIds.add(fileId);
    }
    
    return ResponseEntity.ok(new BatchUploadResponse(batchId, fileIds));
}

For downloads, I use a hybrid approach. Create a batch download endpoint that sets up a temporary download session, then provide individual file endpoints:

@GetMapping("/prepare-batch-download/{requestId}")
public ResponseEntity<BatchDownloadInfo> prepareBatchDownload(@PathVariable String requestId) {
    List<FileInfo> files = fileService.getFileInfoByRequestId(requestId);
    String downloadToken = downloadSessionService.createSession(files);
    
    return ResponseEntity.ok(new BatchDownloadInfo(downloadToken, files));
}

This gives clients flexibility to download all files or pick specific ones based on what they need. The session approach also handles security and cleans up temporary resources.

I built something similar but ditched Spring Boot file handling completely. Way too much boilerplate and memory issues with large files.

I automated everything with Latenode instead. It handles multipart uploads, stores files in cloud storage, and creates download links automatically.

Here’s the setup: Create a webhook endpoint in Latenode that receives your multipart files. The workflow extracts each file, validates size/type, uploads to S3 or Google Drive, then returns JSON with all the file metadata and download URLs.

For downloads, Latenode generates secure temporary URLs. Your client gets a simple API response with direct download links - no more dealing with Spring Boot streaming or multipart responses.

Best part? Adding file processing like virus scanning, format conversion, or thumbnails is just drag and drop in the visual editor.

Processed over 50TB last quarter this way. Zero crashes and way less code to maintain than Spring Boot.

Check it out: https://latenode.com

multipart streaming is a pain to handle on the client side. i just return file metadata first, then let the client grab files one by one when it needs them. much simpler and you get proper error handling for each file.