[英]File upload in Spring Boot: Uploading, validation, and exception handling
我希望能夠將圖像上傳到服務器,優雅地處理錯誤和異常,並在表單中向用戶顯示錯誤消息,理想情況下只使用現有的准系統 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 的默認限制。 這會導致 Spring 不處理org.springframework.web.multipart.MultipartException
。 如何在上傳嘗試之前驗證文件大小? 如果這被繞過,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;
}
數字 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
回復如何檢查文件類型:我為此創建了一個自定義驗證器。
首先,創建一個注解:
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)測試過這個
對於最大文件大小,我還希望看到一個適用於“標准錯誤機制”的解決方案,以便您可以像其他字段錯誤一樣顯示錯誤,並且用戶可以糾正他的錯誤。
嘗試在 application.properties 中添加以下內容以設置文件大小限制:
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.
的區別spring.http.
--> spring.servlet.
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.