'How to limit MultipartFile size per endpoint or controller (NOT globally)?
I want to set a unique file size limit per endpoint or per controller.
I am aware I can set a global file size limit with the following properties, but this is NOT what I want to do, I want UNIQUE LIMIT PER ENDPOINT or CONTROLLER:
spring:
servlet:
multipart:
max-file-size: 1MB
max-request-size: 1MB
I have created a custom validator, but that is not good enough, I want to prevent the user from calling the endpoint rather than throw away files that are too large, I want to prevent the upload.
public class LogoValidator implements ConstraintValidator<ValidLogo, MultipartFile> {
private static final String[] validLogoTypes = new String[]{"image/jpg", "image/jpeg", "image/png", "image/svg"};
private static final long MAX_LOGO_SIZE = 1024L * 1024L;
@Override
public boolean isValid(MultipartFile file, ConstraintValidatorContext context) {
if (file.getSize() > MAX_LOGO_SIZE) {
return false;
}
for(String type : validLogoTypes) {
if (type.equals(file.getContentType())) return true;
}
return false;
}
}
@Documented
@Constraint(validatedBy = LogoValidator.class)
@Target( {ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface ValidLogo {
String message() default "Invalid logo.";
}
Solution 1:[1]
Based on the tips provided by swapyonubuntu, create a bean to register a new filter. Then register your filter, may as well implement inline if this specific filter is only used for a very specific endpoint or two.
@Configuration
public class LogoFilterConfig {
private static final List<String> VALID_LOGO_TYPES = Arrays.asList(
"image/jpeg", "image/jpg", "image/svg", "image/png"
);
private static final long MAX_IMG_SIZE = 2000000;
@Bean
public FilterRegistrationBean<Filter> logoFilter(){
FilterRegistrationBean<Filter> registrationBean = new FilterRegistrationBean<>();
registrationBean.setFilter(new OncePerRequestFilter() {
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain
) throws ServletException, IOException {
if (!VALID_LOGO_TYPES.contains(request.getPart("logo").getContentType())) {
response.sendError(HttpStatus.BAD_REQUEST.value(), "Unsupported media type.");
return;
/*
Returning here means the req/resp is never added to filterChain.
The 'response' is returned to the consumer in its current state,
therefore it's best to modify it for better ux.
*/
}
if (request.getPart("logo").getSize() > MAX_IMG_SIZE) {
response.sendError(HttpStatus.BAD_REQUEST.value(), "Logo too large, max allowed 2MB.");
return;
}
/*
Counterintuitively adding to the filter chain is how you allow the request to go through.
*/
filterChain.doFilter(request, response);
}
});
registrationBean.addUrlPatterns("/my/logo");
registrationBean.setOrder(2);
return registrationBean;
}
}
Solution 2:[2]
You can try doing same using Servlet Filters.Here since you are using spring, OncePerRequestFilter can be great choice.
https://www.baeldung.com/spring-onceperrequestfilter
In filter you can bind size per endpoint. There are various ways to do that eg: you can make it configurable via configuration yaml.
endpoints:
/some/endpt:
supported-types: yaml,image
max-size:300
/some/other/endpt:
supported-types:image
max-size:300
Wire this to a class using configurationproperties https://www.baeldung.com/configuration-properties-in-spring-boot#nested-properties
Initialize at start of filter
Map<String,String> endpointMap = new HashMap<>();
endpointMap.putAll(fromSpringconfigurationpropertiesmap)
//use request.getUrl() or request.getServletPath() based on your requirement to get url
endpointMap.get("urlFromRequest"); // can return url+ supported types.
//reject if size and supported type does not match logic here...
Hope it helps.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Igor Flakiewicz |
| Solution 2 | swapyonubuntu |
