当前位置: 首页 > 知识库问答 >
问题:

Spring Boot中的文件上载:上载、验证和异常处理

谷梁襦宗
2023-03-14

我希望能够将图像上传到服务器,优雅地处理错误和异常,并在表单中向用户显示错误消息,并且理想情况下仅使用现有的准系统Spring Boot和Thymeleaf安装。

使用示例项目gs-上载文件,我可以使用Spring Boot和Thymeleaf将文件上载到服务器。在application.properties中,我设置了spring.http.multipart.max-file-size=1MBspring.http.multipart.max-estest-size=1MB。但是,当我上传大于1MB的文件时,几个安全和验证问题没有解决。

>

  • 可以上载任何文件。例如,可以上载html文件,并将其托管在服务器上。如何按类型限制文件?在发送请求之前,是否可以在页面中验证它们?如果我有多种上传图像的方法,如何验证所有多部分文件?

    用户可以尝试上传大文件,超出Spring和嵌入式Tomcat的默认限制。这会导致org.springframework.web.multipart.MultipartException不是由Spring处理的。如何在上传尝试之前验证文件大小?如果绕过这一点,Spring可以捕获任何文件上传异常,以便显示漂亮的错误消息吗?

    默认的Spring错误页面并没有用作所有异常的回退。MultipartException返回一个带有完整stacktrace的Tomcat异常页面(请参见日志1)。

    我一直在努力寻找并实施一套解决方案。

    修复数字1的一个步骤是修改handleFileUpload,检查内容类型,拒绝未通过此操作的文件:!文件getContentType()。toLowerCase()。用(“图像”)开始。这是否始终有效?恶意用户能否绕过此漏洞?我如何检查每个多部分文件,以避免每次都要记住添加这个文件?

    @PostMapping("/")
    public String handleFileUpload(@RequestParam("file") MultipartFile file, RedirectAttributes redirectAttributes)
            throws MultipartException, IllegalStateException {
    
        if (file != null && file.getContentType() != null && !file.getContentType().toLowerCase().startsWith("image"))
            throw new MultipartException("not img");
    
        storageService.store(file);
        redirectAttributes.addFlashAttribute("message",
                "You successfully uploaded " + file.getOriginalFilename() + "!");
    
        return "redirect:/";
    }
    

    添加异常处理程序(ExceptionHandler)不起作用,它根本不会被调用。

    @ExceptionHandler({ SizeLimitExceededException.class, MultipartException.class,
            java.lang.IllegalStateException.class })
    public ModelAndView handleError(HttpServletRequest req, Exception e) {
        // error("Request: " + req.getRequestURL() + " raised " + ex);
    
        ModelAndView mav = new ModelAndView();
        mav.addObject("exception", e);
        mav.addObject("url", req.getRequestURL());
        mav.addObject("timestamp", new Date());
        mav.addObject("error", e.getClass());
        mav.addObject("message", e.getMessage());
        mav.addObject("status", HttpStatus.INTERNAL_SERVER_ERROR);
        mav.setViewName("error");
        return mav;
    }
    

    3号可以由所有异常的全局异常处理程序来解决。(在这篇文章中详细解释)。但是,我担心它可能会推翻控制器级别的处理程序。

    package hello;
    
    import java.util.Date;
    
    import javax.servlet.http.HttpServletRequest;
    
    import org.springframework.core.annotation.AnnotationUtils;
    import org.springframework.http.HttpStatus;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.bind.annotation.ExceptionHandler;
    import org.springframework.web.bind.annotation.ResponseStatus;
    import org.springframework.web.servlet.ModelAndView;
    
    @ControllerAdvice
    class GlobalDefaultExceptionHandler {
        public static final String DEFAULT_ERROR_VIEW = "error";
    
        @ExceptionHandler(value = Exception.class)
        public ModelAndView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
            // If the exception is annotated with @ResponseStatus rethrow it and let
            // the framework handle it - like the OrderNotFoundException example
            // at the start of this post.
            // AnnotationUtils is a Spring Framework utility class.
            if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null)
                throw e;
    
            // Otherwise setup and send the user to a default error-view.
            ModelAndView mav = new ModelAndView();
            mav.addObject("exception", e);
            mav.addObject("url", req.getRequestURL());
            mav.addObject("timestamp", new Date());
            mav.addObject("error", e.getClass());
            mav.addObject("message", e.getMessage());
            mav.addObject("status", HttpStatus.INTERNAL_SERVER_ERROR);
            mav.setViewName(DEFAULT_ERROR_VIEW);
            return mav;
        }
    }
    

    我尝试了这个答案,它处理异常但返回错误页面。我想返回原始页面并显示一条漂亮的错误消息。

    日志1:

    HTTP Status 500 - Request processing failed; nested exception is org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (1292555) exceeds the configured maximum (1048576)
    
    type Exception report
    
    message Request processing failed; nested exception is org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (1292555) exceeds the configured maximum (1048576)
    
    description The server encountered an internal error that prevented it from fulfilling this request.
    
    exception
    
    org.springframework.web.util.NestedServletException: Request processing failed; nested exception is org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (1292555) exceeds the configured maximum (1048576)
        org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
        org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
        javax.servlet.http.HttpServlet.service(HttpServlet.java:648)
        org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
        javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
        org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
        org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
        org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:89)
        org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
        org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
        org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    root cause
    
    org.springframework.web.multipart.MultipartException: Could not parse multipart servlet request; nested exception is java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (1292555) exceeds the configured maximum (1048576)
        org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:111)
        org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.<init>(StandardMultipartHttpServletRequest.java:85)
        org.springframework.web.multipart.support.StandardServletMultipartResolver.resolveMultipart(StandardServletMultipartResolver.java:76)
        org.springframework.web.servlet.DispatcherServlet.checkMultipart(DispatcherServlet.java:1099)
        org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:932)
        org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:897)
        org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:970)
        org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:872)
        javax.servlet.http.HttpServlet.service(HttpServlet.java:648)
        org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
        javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
        org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
        org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
        org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:89)
        org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
        org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
        org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    root cause
    
    java.lang.IllegalStateException: org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (1292555) exceeds the configured maximum (1048576)
        org.apache.catalina.connector.Request.parseParts(Request.java:2871)
        org.apache.catalina.connector.Request.parseParameters(Request.java:3176)
        org.apache.catalina.connector.Request.getParameter(Request.java:1110)
        org.apache.catalina.connector.RequestFacade.getParameter(RequestFacade.java:381)
        org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:70)
        org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
        org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    root cause
    
    org.apache.tomcat.util.http.fileupload.FileUploadBase$SizeLimitExceededException: the request was rejected because its size (1292555) exceeds the configured maximum (1048576)
        org.apache.tomcat.util.http.fileupload.FileUploadBase$FileItemIteratorImpl.<init>(FileUploadBase.java:811)
        org.apache.tomcat.util.http.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:256)
        org.apache.tomcat.util.http.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:280)
        org.apache.catalina.connector.Request.parseParts(Request.java:2801)
        org.apache.catalina.connector.Request.parseParameters(Request.java:3176)
        org.apache.catalina.connector.Request.getParameter(Request.java:1110)
        org.apache.catalina.connector.RequestFacade.getParameter(RequestFacade.java:381)
        org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:70)
        org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
        org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
    note The full stack trace of the root cause is available in the Apache Tomcat/8.5.5 logs.
    
    Apache Tomcat/8.5.5
    
  • 共有2个答案

    刘升
    2023-03-14

    尝试在应用程序中添加以下内容。设置文件大小限制的属性:

    spring.http.multipart.max-file-size=256KB
    spring.http.multipart.max-request-size=256KB
    

    来源:https://spring.io/guides/gs/uploading-files/

    编辑:自Spring boot 2.0发布以来,属性名称已更改为:

    spring.servlet.multipart.max-file-size=128KB
    spring.servlet.multipart.max-request-size=128KB
    

    注意Spring之间的差异。http --

    巢烨
    2023-03-14

    回复如何检查文件类型:我已经为此创建了一个自定义验证器。

    首先,创建注释:

    import javax.validation.Constraint;
    import javax.validation.Payload;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @Constraint(validatedBy = {ImageFileValidator.class})
    public @interface ValidImage {
        String message() default "Invalid image file";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    }
    

    接下来,创建验证器本身:

    import org.springframework.web.multipart.MultipartFile;
    
    import javax.validation.ConstraintValidator;
    import javax.validation.ConstraintValidatorContext;
    
    public class ImageFileValidator implements ConstraintValidator<ValidImage, MultipartFile> {
    
        @Override
        public void initialize(ValidImage constraintAnnotation) {
    
        }
    
        @Override
        public boolean isValid(MultipartFile multipartFile, ConstraintValidatorContext context) {
    
            boolean result = true;
    
            String contentType = multipartFile.getContentType();
            if (!isSupportedContentType(contentType)) {
                context.disableDefaultConstraintViolation();
                context.buildConstraintViolationWithTemplate(
                        "Only PNG or JPG images are allowed.")
                       .addConstraintViolation();
    
                result = false;
            }
    
            return result;
        }
    
        private boolean isSupportedContentType(String contentType) {
            return contentType.equals("image/png")
                    || contentType.equals("image/jpg")
                    || contentType.equals("image/jpeg");
        }
    }
    

    最后,应用注释:

    public class CreateUserParameters {
    
        @NotNull
        @ValidImage
        private MultipartFile image;
    ...
    }
    

    我已经用Spring Boot 1.5.10(也用Thymeleaf)对此进行了测试

    对于最大文件大小,我还希望看到一个与“标准错误机制”一起工作的解决方案,这样您可以像其他字段错误一样显示错误,用户可以更正错误。

     类似资料:
    • 我正在尝试将文件上传到 Azure Blob 存储,但在将文件推送到存储中时收到错误。 我使用java 11和Quarkus进行开发。在POM上,我添加了工件azure-storage-blob和azure-sdk-bom 法典: 恢复错误 io.net.cha.DefaultChannelPipeline] (vert.x-eventloop-thread-2) 一个 exceptionCaug

    • 我尝试将文件上载到azure web app服务中部署的服务器。在本地,它工作得很好,当谈到azure时,它会抛出一些异常。FileSizeLimitExceededException。 此上载服务是在JAVA Spring boot版本1.5.8中开发的 无法分析多部分servlet请求;嵌套异常为java。lang.IllegalStateException:组织。阿帕奇。公猫util。htt

    • 下面是我的代码: 这里有个例外: 对象“users”中字段“salary”上的字段错误:拒绝值[null];代码[NotNull.Users.Salary,NotNull.Salary,NotNull.java.lang.Integer,NotNull];参数[org.springframework.context.support.defaultmessageSourceResolvable:代码

    • 本文向大家介绍详解SpringBoot文件上传下载和多文件上传(图文),包括了详解SpringBoot文件上传下载和多文件上传(图文)的使用技巧和注意事项,需要的朋友参考一下 最近在学习SpringBoot,以下是最近学习整理的实现文件上传下载的Java代码: 1、开发环境: IDEA15+ Maven+JDK1.8 2、新建一个maven工程:   3、工程框架   4、pom.xml文件依赖项

    • 我希望用户单击一个按钮,一次上载一个文档。 目前,当用户上载文档时,它会将“documents”控件标记为满意。但是,如果用户尝试上载另一个文档,但随后取消,则会将控件标记为不满意。它仍然有第一个文档,但在文件资源管理器上单击“取消”似乎会使验证器认为没有上载任何文档。我怎样才能避开这件事? 在我的html中: 在我的.ts中: 现在,我的upoadDocument()函数对验证没有任何作用。

    • 安装filezilla服务器,并启用设置中的TLS设置的FTP,并启动服务器。通过eclipse java客户端,我试图连接到服务器上传和下载文件,使用下面的代码使用Commons-net apache库。 它在服务器上创建了一个文件hello.txt,但大小为0kb(源文件大小为10kb),并以以下错误结束。请帮助我解决这个问题