1. Einführung
In diesem kurzen Tutorial lernen wir, wie man eine Datei mit Spring Boot hochlädt.
Lass uns sehen, wie wir das durch eine form submission umsetzen können.
2. Warum Datei-Uploads wichtig sind?
Das Hochladen von Dateien ist ein sehr gängiger Anwendungsfall für moderne Webanwendungen, zum Beispiel das Anhängen von Dokumenten in Formularen oder das Hochladen von Profilbildern. Während es einfach zu verstehen ist, bringt die korrekte Verarbeitung von Dateien einige Herausforderungen mit sich. Zum Beispiel kann das Öffnen einer Anwendung ohne Einschränkungen hinsichtlich der Dateigröße, die Benutzer hochladen können, unsere Anwendung überlasten oder den gesamten verfügbaren Speicherplatz verbrauchen. Daher müssen wir, bevor wir eine Datei akzeptieren, ihren Typ und Inhalt validieren und Fehler elegant behandeln.
In unserer Anwendung werden wir die von Spring Boot angebotenen Optionen nutzen, um sie robust zu machen, indem wir eine Grenze für die Dateigröße festlegen und Feedback geben, damit der Benutzer versteht, was passiert.
3. Upload-Fluss
Das folgende Sequenzdiagramm zeigt visuell die Interaktionen zwischen dem Benutzer, dem Browser und unserer Spring Boot-Anwendung:
4. Maven-Konfiguration
Lass uns zunächst unsere Abhängigkeiten einfügen. Stellt sicher, dass wir die neuesten Versionen von spring-boot-starter-web und spring-boot-starter-thymeleaf erhalten.
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>3.4.2</version>
</dependency>
</dependencies>
Für ein neues Projekt ist es am besten, mit dem Spring Initializr zu beginnen und folgende Abhängigkeiten einzufügen:
Spring Web
: MVC-Framework & eingebetteter TomcatThymeleaf
: Eine moderne serverseitige Java-Template-Engine für sowohl Web- als auch Standalone-Umgebungen.
5. Implementierung des Datei-Uploads
Lass uns anfangen, unsere Anwendung zu implementieren.
5.1 Upload-Formular in Thymeleaf
Zuerst werden wir eine Thymeleaf-Vorlage erstellen, die ein einfaches Formular mit einem Datei-Upload-Feld und einem Button zur Einreichung des Formulars an unseren Endpunkt enthält.
Wir müssen eine file-upload.html
-Datei in einem Ordner namens templates
innerhalb des Ressourcenordners der Anwendung erstellen:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/mini.css/3.0.1/mini-default.min.css">
</head>
<body>
<div class="container">
<header>
<h2>File Upload</h2>
</header>
<main>
<form action="#" th:action="@{/upload}" method="post" enctype="multipart/form-data">
<div class="input-group vertical">
<input type="file" name="file" required>
<button type="submit">Upload</button>
</div>
</form>
<div th:if="${message}" class="alert"
th:classappend="${message.contains('Success') ? 'alert-success' : 'alert-error'}"
th:text="${message}"></div>
</main>
</div>
</body>
</html>
enctype=multipart/form-data
hinzuzufügen. Ohne dieses wird der Browser die Datei nicht als multipart-Daten senden, und der Upload wird nicht wie erwartet funktionieren.Nach dem Formular haben wir einen Platzhalter hinzugefügt, um Nachrichten anzuzeigen.
In unserer Vorlage verwenden wir mini.css, damit wir uns nicht mit dem Styling der Anwendung auseinandersetzen müssen, aber dennoch das Standardaussehen des Browsers vermeiden.
Das ist alles für den Front-End-Bereich.
5.2. Speicherkonfiguration
Als nächstes müssen wir entscheiden, wo wir die Dateien speichern, die unsere Benutzer hochladen. Lassen Sie uns eine files.upload-dir
-Eigenschaft in der application.properties
-Datei hinzufügen:
server.port=8090
files.upload-dir=./uploadedfiles
Dies wird der Ort sein, an dem wir die Dateien speichern.
5.3 Datei-Upload-Controller
Jetzt werden wir einen Controller namens FileUploadController
implementieren, der einen Endpunkt bereitstellt, um die Vorlage, die wir erstellt haben, im Browser anzuzeigen:
@Controller
public class FileUploadController {
@GetMapping("/")
public String fileUpload() {
return "file-upload";
}
}
Wir müssen nur sicherstellen, dass der String, den wir zurückgeben, dem Namen der Vorlage entspricht, die wir zuvor erstellt haben.
Als Nächstes werden wir im selben Controller einen POST /upload
Endpunkt erstellen, um Datei-Upload-Anfragen zu verarbeiten. Dieser Endpunkt wird ein MultipartFile
als Anforderungsparameter verwenden, um die Datei zu erhalten. MultipartFile
ist ein Interface, das InputStreamSource
erweitert:
@Value("${files.upload-dir}")
private String uploadDir;
@PostMapping("/upload")
public String handleUpload(
@RequestParam("file") MultipartFile file,
RedirectAttributes redirectAttributes
) {
if (file.isEmpty()) {
redirectAttributes.addFlashAttribute("message", "Please select a file");
return "redirect:/";
}
try {
String filename = StringUtils.cleanPath(file.getOriginalFilename());
Path destination = Paths.get(uploadDir, filename);
Files.createDirectories(destination.getParent());
file.transferTo(destination);
redirectAttributes.addFlashAttribute("message", "Success: " + filename + " (" + file.getSize() + " bytes)");
return "redirect:/";
} catch (IOException e) {
redirectAttributes.addFlashAttribute("message", "Error: " + e.getMessage());
return "redirect:/";
}
}
Kurz gesagt, zuerst erhalten wir den Namen der Datei aus dem Eingabeobjekt file
. Dann erstellen wir die benötigten Ordner, um destination
zu speichern, indem wir StringUtils.cleanPath
verwenden, um einfache Pfad-Traversal-Angriffe zu verhindern. Anschließend speichern wir die eingehenden Dateidaten in destination
und senden eine Erfolgsmeldung. Im Falle eines Fehlers benachrichtigen wir den Benutzer entsprechend, damit er informiert ist, was passiert.
Außerdem haben wir im Controller das Upload-Verzeichnis mit @Value
injiziert und eine @PostMapping
-Methode implementiert, die die MultipartFile
zusammen mit einem RedirectAttributes
-Objekt entgegennimmt. Das RedirectAttributes
-Interface in Spring MVC wird verwendet, um Daten während einer Weiterleitung zu übergeben. Genauer gesagt haben wir addFlashAttribute
verwendet, um die Nachricht vorübergehend in der Sitzung zu speichern, sodass sie nach der Weiterleitung verfügbar ist, wenn die Vorlage verarbeitet wird, und es ermöglicht, das richtige Feedback anzuzeigen.
StringUtils.cleanPath
sollte nicht in einem Sicherheitskontext verwendet werden. Es sollten andere Mechanismen eingesetzt werden, um Probleme mit Path-Traversal zu verhindern.Jetzt, wenn wir die Anwendung mit mvn spring-boot:run
ausführen und zu localhost:8090
gehen, sollten wir unser Formular sehen und in der Lage sein, kleine Dateien hochzuladen.
6. Umgang mit großen Dateien
Wir können jetzt kleine Dateien problemlos hochladen. Wenn die Dateigröße jedoch größer als 1 MB (das Standardlimit) ist, wird unsere Anwendung fehlschlagen, und der Browser könnte einen Fehler anzeigen. Wenn wir Benutzern das Hochladen größerer Dateien ermöglichen möchten, können wir einen höheren Schwellenwert konfigurieren.
6.1 Maximalen Dateigröße konfigurieren
Um die maximal erlaubte Dateigröße zu konfigurieren, ändern wir die Eigenschaften spring.servlet.multipart.max-file-size
und spring.servlet.multipart.max-request-size
in der Datei application.properties
:
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=11MB
Das ist nicht genug für sich allein. Wir müssen auch unseren Servlet-Container konfigurieren, damit er eine Verbindung nicht abbricht, wenn die eingehende Anfrage den standardmäßigen maximalen Größenwert überschreitet, bevor die Multipart-Verarbeitung von Spring einsetzt. Unsere Spring Boot-Anwendung verwendet standardmäßig den eingebetteten Tomcat. Wir können ihn so konfigurieren, dass er größere (oder effektiv unbegrenzte) POST-Anfragen akzeptiert, damit die Größenüberprüfungen ausschließlich durch die Multipart-Konfiguration von Spring behandelt werden.
server.tomcat.max-http-form-post-size=-1
server.tomcat.max-swallow-size=-1
Jetzt, wenn wir versuchen, Dateien größer als 10 MB hochzuladen, wird Spring MVC den Upload verhindern, aber das Standardverhalten kann dennoch zu einer nicht behandelten Fehlerseite führen (wie einer Whitelabel-Fehlerseite, die einen 500-Fehler im Zusammenhang mit MaxUploadSizeExceededException
anzeigt). Wenn wir dieses Verhalten anpassen möchten, um die Interaktion in der Benutzeroberfläche reibungsloser zu gestalten, können wir eine benutzerdefinierte Fehlerbehandlung hinzufügen.
6.2 Benutzerdefinierte Fehlerbehandlung
Hinter den Kulissen wirft Spring MVC eine MaxUploadSizeExceededException
, wenn eine Datei das konfigurierte Größenlimit überschreitet. Wir können ein @ControllerAdvice
erstellen, um MultipartException
global abzufangen und diese Fehler elegant zu behandeln. Wir fangen MultipartException
ab, da MaxUploadSizeExceededException
davon abgeleitet ist, was es uns ermöglicht, auch andere potenzielle, multipart-bezogene Fehler zu behandeln:
@ControllerAdvice
public class FileUploadExceptionHandler {
@ExceptionHandler(MultipartException.class)
public String handleSizeExceeded(RedirectAttributes attrs) {
attrs.addFlashAttribute("message",
"File exceeds size limit!");
return "redirect:/";
}
}
Die Konfiguration eines @ControllerAdvice
bietet einen zentralen Weg, um Ausnahmen über mehrere Controller hinweg zu behandeln und benutzerfreundliche Fehlermeldungen zurückzugeben oder mit entsprechender Rückmeldung weiterzuleiten.
7. Tipps für Produktionsumgebungen
Während dieses Tutorials ein einfacher Datei-Upload erklärt wird, gibt es mehrere zusätzliche Überlegungen für Produktionsumgebungen, die hier nicht behandelt werden.
Zuerst beschränken wir nicht den Typ der Dateien, die hochgeladen werden dürfen. Wir könnten dies mit einer einfachen Überprüfung der Dateiendung lösen oder eine gründlichere Validierung implementieren, wie zum Beispiel die Überprüfung des MIME-Typs oder das Scannen nach Viren und Malware.
In Produktionsumgebungen, in denen große Dateien erwartet werden, sollten wir in Betracht ziehen, einen Streaming-Ansatz zu verwenden, um zu vermeiden, dass die gesamte Datei in den Arbeitsspeicher des Servers geladen wird. Alternativ können wir chunked uploads von der Client-Seite in Betracht ziehen, bei denen die Datei in kleinere Teile aufgeteilt, separat hochgeladen und dann auf der Server-Seite wieder zusammengefügt wird.
8. Fazit
In diesem Artikel haben wir gelernt, wie wir Datei-Uploads in Spring Boot mit einem einfachen HTML-Formular, Thymeleaf und einem Spring MVC-Controller implementieren. Wir haben auch gelernt, wie wir Grenzen für große Dateien konfigurieren und den Fehlerbehandlungsprozess für Größen-Ausnahmen anpassen können.
Sie finden den vollständigen Code auf GitHub.