Here's a comprehensive example of a file management system with Svelte frontend and Spring Boot backend, including file upload, listing, downloading, and deletion functionality.
Project Directory Structure

Backend Implementation (Spring Boot)
1. FileController.java
package com.example.filemanagement.controller;
import com.example.filemanagement.exception.MyFileNotFoundException;
import com.example.filemanagement.model.FileInfo;
import com.example.filemanagement.payload.ResponseMessage;
import com.example.filemanagement.payload.UploadFileResponse;
import com.example.filemanagement.service.FileStorageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.support.ServletUriComponentsBuilder;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@RestController
@RequestMapping("/api/files")
public class FileController {
@Autowired
private FileStorageService fileStorageService;
@PostMapping("/upload")
public ResponseEntity<UploadFileResponse> uploadFile(@RequestParam("file") MultipartFile file) {
String fileName = fileStorageService.storeFile(file);
String fileDownloadUri = ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/api/files/download/")
.path(fileName)
.toUriString();
return ResponseEntity.ok(new UploadFileResponse(
fileName,
fileDownloadUri,
file.getContentType(),
file.getSize()));
}
@PostMapping("/upload-multiple")
public ResponseEntity<List<UploadFileResponse>> uploadMultipleFiles(@RequestParam("files") MultipartFile[] files) {
return ResponseEntity.ok(Arrays.asList(files)
.stream()
.map(file -> uploadFile(file).getBody())
.collect(Collectors.toList());
}
@GetMapping("/download/{fileName:.+}")
public ResponseEntity<Resource> downloadFile(@PathVariable String fileName, HttpServletRequest request) {
Resource resource = fileStorageService.loadFileAsResource(fileName);
String contentType = null;
try {
contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
} catch (IOException ex) {
// Fallback to default content type
contentType = "application/octet-stream";
}
return ResponseEntity.ok()
.contentType(MediaType.parseMediaType(contentType))
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + resource.getFilename() + "\"")
.body(resource);
}
@GetMapping("/list")
public ResponseEntity<List<FileInfo>> listFiles() {
List<FileInfo> fileInfos = fileStorageService.loadAllFiles()
.map(path -> {
String filename = path.getFileName().toString();
String url = ServletUriComponentsBuilder.fromCurrentContextPath()
.path("/api/files/download/")
.path(filename)
.toUriString();
return new FileInfo(filename, url);
})
.collect(Collectors.toList());
return ResponseEntity.ok(fileInfos);
}
@DeleteMapping("/delete/{fileName:.+}")
public ResponseEntity<ResponseMessage> deleteFile(@PathVariable String fileName) {
fileStorageService.deleteFile(fileName);
return ResponseEntity.ok(new ResponseMessage("File deleted successfully: " + fileName));
}
}
2. FileStorageService.java
package com.example.filemanagement.service;
import com.example.filemanagement.exception.FileStorageException;
import com.example.filemanagement.exception.MyFileNotFoundException;
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.stream.Stream;
@Service
public class FileStorageService {
private final Path fileStorageLocation;
public FileStorageService(@Value("${file.upload-dir}") 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 String storeFile(MultipartFile file) {
String fileName = StringUtils.cleanPath(file.getOriginalFilename());
try {
if (fileName.contains("..")) {
throw new FileStorageException("Invalid file path: " + fileName);
}
Path targetLocation = this.fileStorageLocation.resolve(fileName);
Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);
return fileName;
} catch (IOException ex) {
throw new FileStorageException("Could not store file " + fileName, 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 MyFileNotFoundException("File not found: " + fileName);
}
} catch (MalformedURLException ex) {
throw new MyFileNotFoundException("File not found: " + fileName, ex);
}
}
public Stream<Path> loadAllFiles() {
try {
return Files.walk(this.fileStorageLocation, 1)
.filter(path -> !path.equals(this.fileStorageLocation))
.map(this.fileStorageLocation::relativize);
} catch (IOException ex) {
throw new FileStorageException("Failed to read stored files", ex);
}
}
public void deleteFile(String fileName) {
try {
Path filePath = this.fileStorageLocation.resolve(fileName).normalize();
Files.deleteIfExists(filePath);
} catch (IOException ex) {
throw new FileStorageException("Could not delete file: " + fileName, ex);
}
}
}
3. Other Backend Classes
// FileInfo.java (Model)
package com.example.filemanagement.model;
public class FileInfo {
private String name;
private String url;
public FileInfo(String name, String url) {
this.name = name;
this.url = url;
}
// Getters and setters
}
// ResponseMessage.java (Payload)
package com.example.filemanagement.payload;
public class ResponseMessage {
private String message;
public ResponseMessage(String message) {
this.message = message;
}
// Getter and setter
}
// UploadFileResponse.java (Payload)
package com.example.filemanagement.payload;
public class UploadFileResponse {
private String fileName;
private String fileDownloadUri;
private String fileType;
private long size;
public UploadFileResponse(String fileName, String fileDownloadUri, String fileType, long size) {
this.fileName = fileName;
this.fileDownloadUri = fileDownloadUri;
this.fileType = fileType;
this.size = size;
}
// Getters and setters
}
// WebConfig.java (CORS Configuration)
package com.example.filemanagement.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("http://localhost:5000") // Svelte dev server
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true);
}
}
4. application.properties
file.upload-dir=./uploads/
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
spring.servlet.multipart.enabled=true
Frontend Implementation (Svelte)
1. FileUpload.svelte
<script>
import { createEventDispatcher } from 'svelte';
import { files } from '../stores/files';
import api from '../utils/api';
const dispatch = createEventDispatcher();
let selectedFile = null;
let isUploading = false;
let uploadProgress = 0;
let error = null;
const handleFileChange = (e) => {
selectedFile = e.target.files[0];
};
const uploadFile = async () => {
if (!selectedFile) return;
isUploading = true;
error = null;
try {
const formData = new FormData();
formData.append('file', selectedFile);
const response = await api.uploadFile(formData, (progressEvent) => {
uploadProgress = Math.round(
(progressEvent.loaded * 100) / progressEvent.total
);
});
dispatch('uploaded', response);
selectedFile = null;
} catch (err) {
error = err.message || 'Upload failed';
} finally {
isUploading = false;
uploadProgress = 0;
}
};
</script>
<div class="file-upload">
<h2>Upload File</h2>
{#if error}
<div class="error">{error}</div>
{/if}
<div class="file-input">
<input
type="file"
id="file"
on:change={handleFileChange}
disabled={isUploading}
/>
<button
on:click={uploadFile}
disabled={!selectedFile || isUploading}
>
{isUploading ? 'Uploading...' : 'Upload'}
</button>
</div>
{#if isUploading}
<progress value={uploadProgress} max="100">{uploadProgress}%</progress>
{/if}
{#if selectedFile}
<div class="file-info">
<p>Selected file: {selectedFile.name}</p>
<p>Size: {(selectedFile.size / 1024).toFixed(2)} KB</p>
</div>
{/if}
</div>
<style>
.file-upload {
margin: 2rem 0;
padding: 1.5rem;
border: 1px solid #ddd;
border-radius: 8px;
background: #f9f9f9;
}
.error {
color: #ff3e00;
margin-bottom: 1rem;
}
.file-input {
display: flex;
gap: 1rem;
margin-bottom: 1rem;
}
button {
padding: 0.5rem 1rem;
background: #ff3e00;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:disabled {
background: #ccc;
cursor: not-allowed;
}
progress {
width: 100%;
margin: 1rem 0;
}
.file-info {
margin-top: 1rem;
font-size: 0.9rem;
color: #666;
}
</style>
2. FileList.svelte
<script>
import { onMount } from 'svelte';
import { files, fetchFiles } from '../stores/files';
import api from '../utils/api';
let isLoading = false;
let error = null;
onMount(() => {
fetchFiles();
});
const handleDownload = async (fileName, fileUrl) => {
try {
await api.downloadFile(fileUrl, fileName);
} catch (err) {
error = err.message || 'Download failed';
}
};
const handleDelete = async (fileName) => {
try {
await api.deleteFile(fileName);
await fetchFiles();
} catch (err) {
error = err.message || 'Delete failed';
}
};
</script>
<div class="file-list">
<h2>Files</h2>
{#if error}
<div class="error">{error}</div>
{/if}
{#if isLoading}
<div class="loading">Loading files...</div>
{:else}
<table>
<thead>
<tr>
<th>Name</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{#each $files as file}
<tr>
<td>{file.name}</td>
<td class="actions">
<button on:click={() => handleDownload(file.name, file.url)}>
Download
</button>
<button
class="delete"
on:click={() => handleDelete(file.name)}
>
Delete
</button>
</td>
</tr>
{/each}
</tbody>
</table>
{#if $files.length === 0}
<div class="empty">No files uploaded yet.</div>
{/if}
{/if}
</div>
<style>
.file-list {
margin: 2rem 0;
padding: 1.5rem;
border: 1px solid #ddd;
border-radius: 8px;
background: #f9f9f9;
}
.error {
color: #ff3e00;
margin-bottom: 1rem;
}
.loading, .empty {
text-align: center;
padding: 1rem;
color: #666;
}
table {
width: 100%;
border-collapse: collapse;
}
th, td {
padding: 0.75rem;
text-align: left;
border-bottom: 1px solid #ddd;
}
th {
background: #eee;
}
.actions {
display: flex;
gap: 0.5rem;
}
button {
padding: 0.25rem 0.5rem;
background: #ff3e00;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button.delete {
background: #ff0000;
}
</style>
3. files.js (Store)
import { writable } from 'svelte/store';
import api from '../utils/api';
export const files = writable([]);
export const fetchFiles = async () => {
try {
const fileList = await api.getFiles();
files.set(fileList);
} catch (error) {
console.error('Failed to fetch files:', error);
files.set([]);
}
};
4. api.js (API Utility)
const API_BASE = 'http://localhost:8080/api/files';
export default {
async getFiles() {
const response = await fetch(`${API_BASE}/list`);
if (!response.ok) throw new Error('Failed to fetch files');
return await response.json();
},
async uploadFile(file, onProgress) {
const formData = new FormData();
formData.append('file', file);
const xhr = new XMLHttpRequest();
xhr.open('POST', `${API_BASE}/upload`);
return new Promise((resolve, reject) => {
xhr.upload.onprogress = (event) => {
if (event.lengthComputable && onProgress) {
onProgress(event);
}
};
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(JSON.parse(xhr.response));
} else {
reject(new Error(xhr.statusText));
}
};
xhr.onerror = () => reject(new Error('Network error'));
xhr.send(formData);
});
},
async downloadFile(url, fileName) {
const response = await fetch(url);
if (!response.ok) throw new Error('Failed to download file');
const blob = await response.blob();
const downloadUrl = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = downloadUrl;
a.download = fileName;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(downloadUrl);
a.remove();
},
async deleteFile(fileName) {
const response = await fetch(`${API_BASE}/delete/${fileName}`, {
method: 'DELETE'
});
if (!response.ok) throw new Error('Failed to delete file');
return await response.json();
}
};
5. App.svelte
<script>
import FileUpload from './lib/components/FileUpload.svelte';
import FileList from './lib/components/FileList.svelte';
import { fetchFiles } from './lib/stores/files';
const handleUploaded = () => {
fetchFiles();
};
</script>
<main>
<h1>File Management System</h1>
<FileUpload on:uploaded={handleUploaded} />
<FileList />
</main>
<style>
main {
max-width: 800px;
margin: 0 auto;
padding: 2rem;
}
h1 {
color: #ff3e00;
text-align: center;
margin-bottom: 2rem;
}
</style>
Running the Application
- Backend Setup:
- Create the upload directory specified in
application.properties
- Run the Spring Boot application (
mvn spring-boot:run
or through your IDE)
- Create the upload directory specified in
- Frontend Setup:
- Install dependencies:
npm install
- Run the Svelte app:
npm run dev
- Install dependencies:
Key Features Implemented
- File Upload:
- Single file upload with progress tracking
- Multi-file upload capability in backend
- File size validation (10MB limit)
- File Listing:
- Display all uploaded files
- Shows file names and download URLs
- File Download:
- Downloads files with original names
- Handles different content types properly
- File Deletion:
- Removes files from server storage
- Updates UI immediately after deletion
- Error Handling:
- Frontend and backend error handling
- User-friendly error messages
- UI/UX:
- Progress indicators for uploads
- Disabled buttons during operations
- Clean, responsive design
This complete example provides a solid foundation for a file management system that you can extend with additional features like file previews, user authentication, or more advanced file organization.