18

Tengo problemas para capturar y gestionar con gracia la FileUploadBase.SizeLimitExceededException de commons fileupload o MaxUploadSizeExceededException de Spring al cargar archivos de gran tamaño.Uso de Spring 3 @ExceptionHandler con commons FileUpload y SizeLimitExceededException/MaxUploadSizeExceededException

Por lo que puedo decir, estas excepciones se producen durante el enlace de datos, antes de que realmente se alcance el controlador, lo que resulta en un 500 y no se llama al método del controlador de excepciones. ¿Alguien se ha encontrado con esto antes, y cuál es la mejor manera de manejar estas excepciones correctamente?

Respuesta

7

gracias a thetoolman por esta sencilla solución. Lo extendí un poco. Quería dejar el manejo del archivo intacto y transportar la Excepción al Controlador.

package myCompany; 

public class DropOversizeFilesMultipartResolver extends CommonsMultipartResolver { 

    /** 
    * Parse the given servlet request, resolving its multipart elements. 
    * 
    * Thanks Alexander Semenov @ http://forum.springsource.org/showthread.php?62586 
    * 
    * @param request 
    *   the request to parse 
    * @return the parsing result 
    */ 
    @Override 
    protected MultipartParsingResult parseRequest(final HttpServletRequest request) { 

     String encoding = determineEncoding(request); 
     FileUpload fileUpload = prepareFileUpload(encoding); 

     List fileItems; 

     try { 
      fileItems = ((ServletFileUpload) fileUpload).parseRequest(request); 
     } catch (FileUploadBase.SizeLimitExceededException ex) { 
      request.setAttribute(EXCEPTION_KEY, ex); 
      fileItems = Collections.EMPTY_LIST; 
     } catch (FileUploadException ex) { 
      throw new MultipartException("Could not parse multipart servlet request", ex); 
     } 

     return parseFileItems(fileItems, encoding); 
    } 
} 

y en el controlador

@InitBinder("fileForm") 
    protected void initBinderDesignForm(WebDataBinder binder) { 
    binder.setValidator(new FileFormValidator()); 
    } 

    @RequestMapping(value = "/my/mapping", method = RequestMethod.POST) 
    public ModelAndView acceptFile(HttpServletRequest request, Model model, FormData formData, 
     BindingResult result) { 

    Object exception = request.getAttribute(DropOversizeFilesMultipartResolver.EXCEPTION_KEY); 
    if (exception != null && FileUploadBase.SizeLimitExceededException.class.equals(exception.getClass())) { 
     result.rejectValue("file", "<your.message.key>"); 
     LOGGER.error(exception); 
    } 

la config resorte sigue siendo el mismo. Sería muy agradable tener la excepción transportada al validador, pero aún no he descubierto cómo hacerlo.

+0

Tenga en cuenta que al establecer 'fileItems = Collections.EMPTY_LIST;', todos los parámetros de solicitud se descartan. En otras palabras, 'request.getParameterMap()' será '{}'. –

+2

El comentario de @Markus es especialmente importante a tener en cuenta si uno de los parámetros de solicitud es un token csrf. Al establecer 'fileItems = Collections.EMPTY_LIST;' se descarta el token CSRF, haciendo que el filtro CSRF trate esta solicitud como inválida. –

1

Esto parece ser un problema bastante común. He tenido problemas similares y preguntas similares, como por ejemplo this question. Todavía tengo que ver una buena solución al problema. Puede usar un filtro de servlet de vanilla para manejar estas excepciones, pero eso duplicará el manejo de su error ya que ya tiene un ExceptionHandler.

+0

Sí, la pregunta que vinculó fue una de las muchas que encontré en mi investigación donde se formula la pregunta, pero no se encontró una solución limpia. Esperaba que hubiera una manera más agradable, pero estoy de acuerdo en que un filtro o incluso un contenedor para el resolvedor podría ser la única manera. – Luke

4

Sé que esto es viejo, pero estaba buscando una solución para esto también y no pude encontrar nada. Estamos proporcionando servicios RESTful utilizando Spring y estamos cargando archivos y no estábamos seguros de cómo manejar esto. Se me ocurrió lo siguiente y espero que sea útil a alguien:

Todos nuestros excepciones se manejan con anotaciones, así que tenemos puesta en marcha nuestra resolución de manipulador de error como este:

@Configuration 
public class MyConfig{ 

    @Bean 
    public AnnotationMethodHandlerExceptionResolver exceptionResolver(){ 

     final AnnotationMethodHandlerExceptionResolver resolver = new AnnotationMethodHandlerExceptionResolver(); 
     resolver.setMessageConverters(messageConverters()); 
     resolver; 
    } 
} 

A continuación, una común clase que puede manejar la excepción

public class MultipartExceptionHandler 
{ 

    @ExceptionHandler(MaxUploadSizeExceededException.class) 
    @ResponseStatus(value = HttpStatus.PRECONDITION_FAILED) 
    @ResponseBody 
    protected CustomError handleMaxUploadSizeExceededException(final HttpServletRequest request, 
      final HttpServletResponse response, final Throwable e) 
      throws IOException 
    { 
     logger.error(e); 
     CustomError c = new CustomErrorMaxFileSize("Max file size exceeded", MAX_FILE_SIZE); 
     return c; 
    } 

    @ExceptionHandler(MultipartException.class) 
    @ResponseStatus(value = HttpStatus.INTERNAL_SERVER_ERROR) 
    @ResponseBody 
    protected CustomError handleGenericMultipartException(final HttpServletRequest request, 
      final HttpServletResponse response, final Throwable e) 
      throws IOException 
    { 
     logger.error(e); 
     CustomError c = new CustomErrorGeneric("There was a problem with the upload"); 
     return c; 
    } 
} 

Luego subclase del patrimonio común de resolución de varias partes e implementar la interfaz HandlerExceptionResolver

@Component(value="multipartResolver") // Spring expects this name 
public class MyMultipartResolver extends CommonsMultipartResolver implements HandlerExceptionResolver 
{ 

    // This is the Spring bean that handles exceptions 
    // We defined this in the Java configuration file 
    @Resource(name = "exceptionResolver") 
    private AnnotationMethodHandlerExceptionResolver exceptionResolver; 

    // The multipart exception handler with the @ExceptionHandler annotation 
    private final MultipartExceptionHandler multipartExceptionHandler = new MultipartExceptionHandler(); 

    // Spring will call this when there is an exception thrown from this 
    // multipart resolver 
    @Override 
    public ModelAndView resolveException(
      final HttpServletRequest request, 
      final HttpServletResponse response, 
      final Object handlerParam, 
      final Exception ex) 
    { 

     // Notice that we pass this.multipartExceptionHandler 
     // and not the method parameter 'handlerParam' into the 
     // exceptionResolver. We do this because the DispatcherServlet 
     // doDispatch() method calls checkMultipart() before determining 
     // the handler for the request. If doing the multipart check fails 
     // with a MultipartException, Spring will never have a reference 
     // to the handler and so 'handlerParam' will be null at this point. 
     return exceptionResolver.resolveException(request, response, this.multipartExceptionHandler, ex); 

    } 
} 
Cuestiones relacionadas