Aller au contenu

Comment télécharger un fichier dans Spring Boot

Spring Spring-Boot File-Upload Thymeleaf
Auteur
Harpal Singh
Software Engineer
Traduit par
Namastecode
Sommaire

1. Introduction

Dans ce tutoriel rapide, nous allons apprendre comment télécharger un fichier en utilisant Spring Boot.

Voyons comment le faire à travers une soumission de formulaire.

2. Pourquoi les téléchargements de fichiers sont-ils importants ?

Le téléchargement de fichiers est un cas d’utilisation très courant pour les applications web modernes, par exemple, l’ajout de documents dans des formulaires ou le téléchargement de photos de profil. Bien que cela soit simple à comprendre, gérer des fichiers correctement présente certaines difficultés. Par exemple, exposer une application sans contraintes sur la taille des fichiers que les utilisateurs peuvent télécharger peut submerger notre application ou consommer tout le stockage disponible. Par conséquent, avant d’accepter un fichier, nous devons valider son type et son contenu et gérer les échecs avec aisance.

Donc, dans notre application, nous allons utiliser les options offertes par Spring Boot pour la rendre robuste en imposant une limite sur la taille des fichiers et en fournissant un retour afin que l’utilisateur comprenne ce qui se passe.

3. Flux de Téléversement

Le diagramme de séquence suivant montre visuellement les interactions entre l’Utilisateur, le Navigateur et notre application Spring Boot :

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

Sorry, it seems there was no content provided for translation. Please provide the text you would like me to translate into French.

4. Configuration de Maven

Commençons par inclure nos dépendances. Assurons-nous d’obtenir les dernières versions de spring-boot-starter-web et spring-boot-starter-thymeleaf.

<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>

Pour un nouveau projet, il est préférable de commencer par le Spring Initializr et d’inclure ces dépendances :

  • Spring Web : framework MVC et Tomcat intégré
  • Thymeleaf : Un moteur de templates Java moderne côté serveur pour les environnements web et autonomes.
Astuce : Utilisez Spring Boot DevTools pour des redémarrages automatiques pendant le développement.

5. Mise en œuvre du téléchargement de fichiers

Commençons à mettre en œuvre notre application.

5.1 Formulaire de téléversement dans Thymeleaf

Tout d’abord, nous allons créer un modèle Thymeleaf qui comprend un formulaire de base avec un champ de fichier et un bouton pour soumettre le formulaire à notre point de terminaison.

Nous devons créer un fichier file-upload.html dans un dossier appelé templates au sein du dossier des ressources de l’application :

<!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>
Une erreur très courante consiste à oublier d’ajouter l’attribut enctype=multipart/form-data. Sans cela, le navigateur n’enverra pas le fichier sous forme de données multipart, et le téléchargement ne fonctionnera pas comme prévu.

Après le formulaire, nous avons ajouté un espace réservé pour afficher des messages.

Dans notre modèle, nous utilisons mini.css afin de ne pas avoir à gérer le style de l’application tout en évitant l’apparence par défaut du navigateur.

C’est tout pour le côté front-end.

5.2. Configuration du stockage

Ensuite, nous devons décider où enregistrer les fichiers téléchargés par nos utilisateurs. Ajoutons une propriété files.upload-dir dans le fichier application.properties :

server.port=8090
files.upload-dir=./uploadedfiles

C’est l’endroit où nous allons sauvegarder les fichiers.

5.3 Contrôleur de Téléversement de Fichier

Maintenant, nous allons implémenter un contrôleur nommé FileUploadController qui expose un point de terminaison pour servir le modèle que nous avons créé au navigateur :

@Controller
public class FileUploadController {

    @GetMapping("/")
    public String fileUpload() {
        return "file-upload";
    }

}

Nous devons simplement nous assurer que la chaîne que nous retournons correspond au nom du modèle que nous avons créé précédemment.

Ensuite, dans le même contrôleur, nous allons créer un point de terminaison POST /upload pour gérer les demandes de téléchargement de fichiers. Ce point de terminaison utilisera un MultipartFile comme paramètre de requête pour obtenir le fichier. MultipartFile est une interface qui étend InputStreamSource :

@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:/";
    }
}

En bref, d’abord, nous obtenons le nom du fichier à partir de l’objet d’entrée file. Ensuite, nous créons les dossiers nécessaires pour enregistrer destination en utilisant StringUtils.cleanPath afin de prévenir les attaques simples de traversée de chemin. Ensuite, nous enregistrons les données du fichier entrant dans destination et envoyons un message de succès. En cas d’échec, nous informons l’utilisateur en conséquence, juste pour qu’il sache ce qui se passe.

Dans le contrôleur, nous avons également injecté le répertoire de téléchargement en utilisant @Value et implémenté une méthode @PostMapping qui prend le MultipartFile ainsi qu’un objet RedirectAttributes. L’interface RedirectAttributes dans Spring MVC est utilisée pour passer des données lors d’une redirection. Plus précisément, nous avons utilisé addFlashAttribute pour sauvegarder le message dans la session de manière temporaire, afin qu’il soit disponible après la redirection lorsque le template est traité, permettant ainsi d’afficher le retour d’information correct.

StringUtils.cleanPath ne doit pas être utilisé comme une méthode de sécurité. D’autres mécanismes doivent être employés pour prévenir les problèmes de traversée de chemin.

Maintenant, si nous exécutons l’application en utilisant mvn spring-boot:run et que nous allons sur localhost:8090, nous devrions voir notre formulaire et être en mesure de télécharger des fichiers de petite taille.

6. Gestion des gros fichiers

Nous pouvons maintenant télécharger des petits fichiers sans aucun problème. Mais si la taille du fichier est supérieure à 1 Mo (la limite par défaut), notre application échouera, et le navigateur pourrait afficher une erreur. Si nous voulons permettre aux utilisateurs de télécharger des fichiers plus volumineux, nous pouvons configurer un seuil plus élevé.

6.1 Configurer la taille maximale des fichiers

Pour configurer la taille de fichier maximale autorisée, nous modifions les propriétés spring.servlet.multipart.max-file-size et spring.servlet.multipart.max-request-size dans application.properties :

spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=11MB
Erreur courante : La taille de la requête doit être supérieure à la taille du fichier pour tenir compte de la surcharge des données du formulaire.

Cela ne suffit pas à lui seul. Nous devons également configurer notre conteneur de servlets afin qu’il n’abandonne pas une connexion si la requête entrante dépasse sa valeur maximale par défaut avant que la gestion des multipart de Spring ne prenne effet.
Notre application Spring Boot utilise par défaut Tomcat intégré. Nous pouvons le configurer pour accepter des requêtes POST de taille plus grande (ou effectivement illimitée) afin que les vérifications de taille soient gérées uniquement par la configuration multipart de Spring :

server.tomcat.max-http-form-post-size=-1
server.tomcat.max-swallow-size=-1
Préoccupations de sécurité : Définir des limites à -1 n’est généralement pas recommandé car cela permet des requêtes d’une taille potentiellement illimitée, ce qui peut entraîner des attaques par déni de service (DoS). Idéalement, nous devrions limiter la taille de la requête à une valeur raisonnable appropriée au cas d’utilisation de l’application.

Maintenant, si nous essayons de télécharger des fichiers de plus de 10 Mo, Spring MVC empêchera le téléchargement, mais le comportement par défaut pourrait toujours aboutir à une page d’erreur non gérée (comme une page d’erreur Whitelabel affichant une erreur 500 liée à MaxUploadSizeExceededException). Si nous souhaitons personnaliser ce comportement afin que l’interaction dans l’UI soit plus fluide, nous pouvons ajouter une gestion des erreurs personnalisée.

6.2 Gestion des erreurs personnalisée

En arrière-plan, Spring MVC déclenche une MaxUploadSizeExceededException lorsqu’un fichier dépasse la limite de taille configurée. Nous pouvons créer un @ControllerAdvice pour attraper MultipartException globalement et gérer ces erreurs avec élégance. Nous attrapons MultipartException car MaxUploadSizeExceededException l’étend, nous permettant de traiter d’autres erreurs potentielles liées au multipart également :

@ControllerAdvice
public class FileUploadExceptionHandler {

    @ExceptionHandler(MultipartException.class)
    public String handleSizeExceeded(RedirectAttributes attrs) {
        attrs.addFlashAttribute("message",
            "File exceeds size limit!");
        return "redirect:/";
    }

}

La configuration d’un @ControllerAdvice offre un moyen centralisé de gérer les exceptions à travers plusieurs contrôleurs et de renvoyer des messages d’erreur conviviaux ou de rediriger avec un retour d’information approprié.

7. Conseils pour les Environnements de Production

Bien que ce tutoriel explique un téléchargement de fichier simple, il existe plusieurs considérations supplémentaires pour les environnements de production qui ne sont pas abordées ici.

Tout d’abord, nous ne limitons pas le type de fichiers autorisés pour le téléchargement. Nous pourrions résoudre cela par une simple vérification de l’extension de fichier ou mettre en œuvre une validation plus approfondie, comme vérifier le type MIME ou scanner à la recherche de virus et de logiciels malveillants.

Dans des environnements de production où de grands fichiers sont attendus, nous devrions envisager d’utiliser une approche de streaming pour éviter de charger l’intégralité du fichier en mémoire sur le serveur. En alternative, nous pourrions considérer l’utilisation de téléversements par morceaux depuis le côté client, où le fichier est divisé en parties plus petites, téléversées séparément, puis réassemblées côté serveur.

8. Conclusion

Dans cet article, nous avons appris à implémenter le téléchargement de fichiers dans Spring Boot en utilisant un simple formulaire HTML, Thymeleaf et un contrôleur Spring MVC. Nous avons également découvert comment configurer des limites pour les fichiers volumineux et personnaliser le processus de gestion des erreurs pour les exceptions de taille.

Vous pouvez trouver le code complet sur GitHub.

Articles connexes

Requêtes Http avec Rest Client dans le Spring Framework 6
Spring Restclient HTTP Rest Openai
Comprendre la syntaxe Cron de @Scheduled de Spring
Spring Cron Scheduling
Testcontainers dans les tests d'intégration Spring Boot
Spring Testcontainers Testing