Spring Boot中的文件上传:上传,验证和异常处理
我希望能够将图像上传到服务器,优雅地处理错误和异常,并在表单中向用户显示错误消息,理想情况下只使用现有的准系统Spring Boot和Thymeleaf安装。
使用示例项目gs-uploading-files,我可以使用Spring Boot和Thymeleaf将文件上传到服务器。 在application.properties中,我设置了spring.http.multipart.max-file-size=1MB
和spring.http.multipart.max-request-size=1MB
。 但是,当我上传大于1MB的文件时,有几个安全和验证问题未解决。
任何文件都可以上传。 例如,一个html文件可以被上传并因此托管在服务器上。 文件如何按类型限制? 在请求发送之前,他们可以在页面中进行验证吗? 如果我有多种上传图片的方式,我如何验证所有MultipartFiles?
用户可以尝试上传大文件,超出Spring和嵌入式Tomcat的默认限制。 这会导致org.springframework.web.multipart.MultipartException
不由Spring处理。 如何在上传尝试之前验证文件大小? 在这种情况下,Spring可以捕获任何文件上传异常,以便显示一条好的错误消息?
默认的Spring错误页面不被用作所有异常的后备。 MultipartException以完整的堆栈跟踪返回一个Tomcat异常页面(参见日志1)。
我试图找到并实施一套解决方案。
修复数字1的一个步骤是修改handleFileUpload
检查内容类型,并拒绝文件失败: !file.getContentType().toLowerCase().startsWith("image")
。 这会永远有效吗? 恶意用户可以绕过这个吗? 我怎样才能检查每个MultipartFile,保存每次都必须记住添加这个?
@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;
}
所有异常都可以由全局异常处理程序解决。 (在这篇文章中详细解释)。 不过,我担心它可能会否决控制器级别的处理程序。
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
尝试在application.properties中添加以下内容来设置文件大小限制:
spring.http.multipart.max-file-size=256KB
spring.http.multipart.max-request-size=256KB
来源:https://spring.io/guides/gs/uploading-files/
回复如何检查文件类型:我为此创建了一个自定义验证器。
首先,创建一个注释:
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)
对于最大文件大小,我还希望看到一个与“标准错误机制”配合使用的解决方案,以便像其他字段错误一样显示错误,并且用户可以纠正错误。
链接地址: http://www.djcxy.com/p/48727.html上一篇: File upload in Spring Boot: Uploading, validation, and exception handling
下一篇: 415 (Unsupported Media type) when trying to post formData to Spring