6 votes

Comment attraper l'exception FileSizeLimitExceededException dans une action de contrôleur multipart de Spring Boot dans un conteneur Tomcat ?

Lorsque je télécharge un fichier dont la taille dépasse la taille maximale configurée, la réponse renvoyée n'est pas très jolie ni utile pour mon interface utilisateur JS. Je veux donc l'attraper et la traiter. Mais, le problème est que l'erreur est lancée avant que mon contrôleur soit entré. Je ne sais donc pas où placer mon code de gestion des erreurs. Une idée que je caresse est de définir un filtre et de l'attraper à cet endroit. Est-ce l'endroit normal pour le faire ? La trace de la pile que je vois est la suivante :

at org.apache.tomcat.util.http.fileupload.FileUploadBase$FileItemIteratorImpl$FileItemStreamImpl$1.raiseError(FileUploadBase.java:628) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.tomcat.util.http.fileupload.util.LimitedInputStream.checkLimit(LimitedInputStream.java:76) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.tomcat.util.http.fileupload.util.LimitedInputStream.read(LimitedInputStream.java:135) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at java.io.FilterInputStream.read(FilterInputStream.java:107) ~[na:1.8.0_121]
at org.apache.tomcat.util.http.fileupload.util.Streams.copy(Streams.java:98) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.tomcat.util.http.fileupload.util.Streams.copy(Streams.java:68) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.tomcat.util.http.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:293) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.connector.Request.parseParts(Request.java:2902) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.connector.Request.parseParameters(Request.java:3242) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.connector.Request.getParameter(Request.java:1136) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.connector.RequestFacade.getParameter(RequestFacade.java:381) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:84) ~[spring-web-5.0.9.RELEASE.jar!/:5.0.9.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.9.RELEASE.jar!/:5.0.9.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) ~[spring-web-5.0.9.RELEASE.jar!/:5.0.9.RELEASE]
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) ~[spring-web-5.0.9.RELEASE.jar!/:5.0.9.RELEASE]
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) ~[tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:806) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) [na:1.8.0_121]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) [na:1.8.0_121]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.34.jar!/:8.5.34]
at java.lang.Thread.run(Thread.java:745) [na:1.8.0_121]

L'action de mon contrôleur ressemble à ceci :

@PostMapping("/upload")
@ResponseBody
public String handleFileUpload(@RequestParam("file") MultipartFile file) {
    String fileName = storageService.store(file);
    String fileUrl = "/api/file/" + fileName;

    return "{\"fileUrl\":\"" + fileUrl + "\"}";
}

Mais ce n'est pas très important car ce code n'est pas saisi parce que l'exception est levée dans le fil d'abord.

Comme prévu, l'ajout d'un gestionnaire d'exception au contrôleur n'a pas permis d'attraper l'exception pour la même raison.

@ExceptionHandler(FileUploadBase.FileSizeLimitExceededException.class)
public String handlefileSizeLimitExceeded(FileUploadBase.FileSizeLimitExceededException exc) {
    return "{\"error\":\"file too big\"}";
}

Remarque : je ne demande pas comment modifier la taille maximale des fichiers. Je sais déjà comment le faire. Mon objectif est de signaler lorsque l'utilisateur tente de télécharger un fichier dont la taille est supérieure au maximum.

2voto

HoucineKrichen Points 21

Vous devez attraper MaxUploadSizeExceededException.class.

@ExceptionHandler(MaxUploadSizeExceededException.class)
public String handleFileSizeLimitExceeded(MaxUploadSizeExceededException exc) {
    return "{\"error\":\"file too big\"}";
}

0voto

loïc Points 457

J'ai eu le même problème, l'autre réponse n'a pas fonctionné pour moi alors j'ai continué à chercher et j'ai trouvé ceci : https://www.baeldung.com/spring-maxuploadsizeexceeded (comme toujours, excellents guides sur baeldung.com, merci !). Vérifiez la partie configuration de Tomcat, elle explique pourquoi nous rencontrons toujours le problème même en utilisant un fichier ExceptionHandler

Je vais partager ce qui a fonctionné pour moi, en utilisant Spring Boot v2.3.4 avec le Tomcat embarqué. Je voulais autoriser les fichiers de 10 MB et montrer un message d'erreur à l'utilisateur

Ajouté à application.yml

#Configuration for size of files that can be uploaded
#Set Tomcat to accept any file size for failed upload
server.tomcat.max-swallow-size: -1
#set the Spring max file size for upload
spring.servlet.multipart:
    max-file-size: 10MB
    max-request-size: 10MB

Puis j'ai ajouté un @ControllerAdvice comme l'a dit le guide. Notez que le simple fait d'ajouter un @ExceptionHandler au contrôleur où le téléchargement est géré n'a pas fonctionné.

@Slf4j
@ControllerAdvice
public class FileUploadExceptionAdvice {

    @ExceptionHandler(MaxUploadSizeExceededException.class)
    public ResponseEntity<String> handleMaxUploadSizeExceededException(Exception ex){
        log.warn("Handling MaxUploadSizeExceededException: {}",ex.getMessage());

        return ResponseEntity.status(HttpStatus.PAYLOAD_TOO_LARGE)
                .contentType(MediaType.TEXT_PLAIN)
                .body("Message for user");
    }
}

Dans mon cas, je voulais renvoyer un texte simple à utiliser dans la bibliothèque JS qui gère le téléchargement et affiche l'erreur (celle-ci est excellente) : https://www.dropzonejs.com/ ).

Vous pouvez changer votre traitement en fonction de ce dont vous avez besoin : un message d'erreur JSON, ou afficher une page avec un message d'erreur comme le guide.

Notez que vous pouvez récupérer le Locale dans le gestionnaire d'exception, si vous avez besoin d'une traduction pour votre message d'erreur, ajoutez-la simplement comme paramètre de la méthode. Cela ressemble à quelque chose comme ça :

@Slf4j
@ControllerAdvice
public class FileUploadExceptionAdvice {
    @Value("${spring.servlet.multipart.max-file-size}")
    private String maxFileSize;

    private final MessageSource messageSource;

    public FileUploadExceptionAdvice(MessageSource messageSource){
        this.messageSource=messageSource;
    }

 @ExceptionHandler(MaxUploadSizeExceededException.class)
    public ResponseEntity<String> handleMaxUploadSizeExceededException(Locale locale, Exception ex){

        String errorMessage = messageSource.getMessage("max.upload.size.exceeded", new Object[]{maxFileSize}, locale);
//use errorMessage in return handling
   }
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X