Zum Hauptinhalt springen

Wie man eine Datei in Spring Boot hochlädt

Spring Spring-Boot File-Upload Thymeleaf
Autor
Harpal Singh
Software Engineer
Übersetzt von
Namastecode
Inhaltsverzeichnis

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:

sequenceDiagram participant User as User participant Browser as Browser participant Controller as UploadController participant Filesystem as File System User->>Browser: Selects file and submits form Browser->>Controller: POST /upload (multipart/form-data) Controller->>Controller: Check if file is empty alt File is valid Controller->>Filesystem: Save file bytes to disk Controller-->>Browser: Redirect to / with success message else File is invalid Controller-->>Browser: Redirect to / with error message end Browser->>User: Display upload status message


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 Tomcat
  • Thymeleaf: Eine moderne serverseitige Java-Template-Engine für sowohl Web- als auch Standalone-Umgebungen.
Tipp: Verwenden Sie Spring Boot DevTools für automatische Neustarts während der Entwicklung.

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>
Ein sehr häufiger Fehler ist das Vergessen, das Attribut 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
Häufiger Fehler: Die Anforderungsgröße muss größer sein als die Dateigröße, um den Überhang an Formulardaten zu berücksichtigen.

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
Sicherheitsbedenken: Das Setzen von Grenzen auf -1 wird allgemein nicht empfohlen, da dies potenziell unbegrenzte Größenanforderungen erlaubt, die zu Denial of Service (DoS) Angriffen führen können. Ideal wäre es, die Größe der Anfrage auf einen angemessenen Wert zu beschränken, der dem Anwendungsfall der App entspricht.

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.

Verwandte Artikel

Http-Anfragen mit Rest Client im Spring Framework 6
Spring Restclient HTTP Rest Openai
Testcontainers in Spring Boot Integrationstests
Spring Testcontainers Testing
Werte aus einer Properties-Datei in Spring abrufen
Spring Properties Basics