package com.example.demo.controller;
import com.example.demo.dto.FileMetadata;
import com.example.demo.service.FileStorageService;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import jakarta.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.List;
@RestController
@RequestMapping("/api/files")
public class FileUploadController {
private final FileStorageService fileStorageService;
public FileUploadController(FileStorageService fileStorageService) {
this.fileStorageService = fileStorageService;
}
@PostMapping("/upload")
public ResponseEntity<FileMetadata> uploadFile(
@RequestParam("file") MultipartFile file
) {
FileMetadata metadata = fileStorageService.storeFile(file);
return ResponseEntity.ok(metadata);
}
@PostMapping("/upload-multiple")
public ResponseEntity<List<FileMetadata>> uploadMultipleFiles(
@RequestParam("files") List<MultipartFile> files
) {
List<FileMetadata> metadataList = files.stream()
.map(fileStorageService::storeFile)
.toList();
return ResponseEntity.ok(metadataList);
}
@GetMapping("/download/{fileName:.+}")
public ResponseEntity<Resource> downloadFile(
@PathVariable String fileName,
HttpServletRequest request
) throws IOException {
Resource resource = fileStorageService.loadFileAsResource(fileName);
String contentType = request.getServletContext()
.getMimeType(resource.getFile().getAbsolutePath());
if (contentType == null) {
contentType = "application/octet-stream";
}
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(contentType))
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + resource.getFilename() + "\"")
.body(resource);
}
@DeleteMapping("/{fileName:.+}")
public ResponseEntity<Void> deleteFile(@PathVariable String fileName) {
fileStorageService.deleteFile(fileName);
return ResponseEntity.noContent().build();
}
}
package com.example.demo.service;
import com.example.demo.dto.FileMetadata;
import com.example.demo.exception.FileStorageException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.UUID;
@Service
public class FileStorageService {
private final Path fileStorageLocation;
public FileStorageService(@Value("${file.upload-dir:uploads}") String uploadDir) {
this.fileStorageLocation = Paths.get(uploadDir).toAbsolutePath().normalize();
try {
Files.createDirectories(this.fileStorageLocation);
} catch (Exception ex) {
throw new FileStorageException("Could not create upload directory", ex);
}
}
public FileMetadata storeFile(MultipartFile file) {
String originalFileName = StringUtils.cleanPath(file.getOriginalFilename());
// Validate file
if (originalFileName.contains("..")) {
throw new FileStorageException("Invalid file path: " + originalFileName);
}
if (file.isEmpty()) {
throw new FileStorageException("Cannot store empty file");
}
// Generate unique filename
String extension = originalFileName.substring(originalFileName.lastIndexOf("."));
String storedFileName = UUID.randomUUID().toString() + extension;
try {
Path targetLocation = this.fileStorageLocation.resolve(storedFileName);
Files.copy(file.getInputStream(), targetLocation,
StandardCopyOption.REPLACE_EXISTING);
FileMetadata metadata = new FileMetadata();
metadata.setFileName(storedFileName);
metadata.setOriginalFileName(originalFileName);
metadata.setContentType(file.getContentType());
metadata.setSize(file.getSize());
return metadata;
} catch (IOException ex) {
throw new FileStorageException("Failed to store file " + originalFileName, ex);
}
}
public Resource loadFileAsResource(String fileName) {
try {
Path filePath = this.fileStorageLocation.resolve(fileName).normalize();
Resource resource = new UrlResource(filePath.toUri());
if (resource.exists()) {
return resource;
} else {
throw new FileStorageException("File not found: " + fileName);
}
} catch (MalformedURLException ex) {
throw new FileStorageException("File not found: " + fileName, ex);
}
}
public void deleteFile(String fileName) {
try {
Path filePath = this.fileStorageLocation.resolve(fileName).normalize();
Files.deleteIfExists(filePath);
} catch (IOException ex) {
throw new FileStorageException("Failed to delete file: " + fileName, ex);
}
}
}
Spring Boot handles multipart file uploads efficiently. MultipartFile represents uploaded files. I validate file types, sizes, and content. Files are stored locally, in cloud storage (S3, Azure Blob), or databases. Streaming large files prevents memory issues. Content-Disposition headers enable downloads with custom filenames. MIME types ensure proper browser handling. Virus scanning protects against malware. Asynchronous processing handles large uploads. Temporary file cleanup prevents disk space issues. Configuration limits max file size and request size. File metadata—original name, content type, size—is persisted. Proper error handling covers upload failures, storage issues, and validation errors. Security considerations include path traversal prevention and access control.
Related snips
type User {
id: ID!
name: String!
email: String!
posts: [Post!]!
createdAt: String!
GraphQL API with Spring Boot
package com.example.starter.config;
import com.example.starter.properties.CustomProperties;
import com.example.starter.service.CustomService;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
Custom Spring Boot starters
# Installation
# rails active_storage:install
# rails db:migrate
# config/storage.yml
local:
ActiveStorage for file uploads and attachments
package com.example.demo.config;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.apache.kafka.common.serialization.StringSerializer;
Messaging with Apache Kafka
# Headless Service for stable DNS
apiVersion: v1
kind: Service
metadata:
name: postgres
namespace: production
Kubernetes StatefulSets for stateful workloads
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(100) NOT NULL UNIQUE,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
Database migration with Flyway
Share this code
Here's the card — post it anywhere.