Search code examples
javaspringspring-bootmultipart

How to change spring.http.multipart.maxFileSize in Runtime?


In application.properties I have:

spring.http.multipart.maxFileSize
spring.http.multipart.maxRequestSize

But I want to have possibility to change this property without application restart.

Is it possible ?


Solution

  • After digging in source code i found a way how to do it for StandardServletMultipartResolver.

    StandardServletMultipartResolver wraps HttpServletRequest with StandardMultipartHttpServletRequest which uses standard servlet api implementation of request.getParts() for processing multipart request. Internally this method uses MultipartConfigElement which is set by ServletRegistrationBean during registration of dispatcher servlet. We are interested in MultipartConfigElement cause it contains properties we are interested in.

    By default ServletRegistrationBean gets MultipartConfigElement from MultipartAutoConfiguration. Default implementation of MultipartConfigElement doesn't allow to modify its properties so we need to extend this class to add such ability. Further implementation depends on whether you want to modify parameters on per request basis or time to time for the whole application.

    Per request basis

    MultipartConfigurationContext stores configuration for a particular request. UpdateMultipartConfigurationFilter sets multipart properties for given request. UpdatableMultipartConfigElement uses this information if provided.

    public class MultipartConfigurationContext {
    
        private static ThreadLocal<Integer> maxFileSize = new ThreadLocal<>();
    
        public static void setMaxFileSize(Integer val) {
            maxFileSize.set(val);
        }
    
        public static Integer getMaxFileSize() {
            return maxFileSize.get();
        }
    
        public static void clear() {
            maxFileSize.set(null);
        }
    }
    
    public class UpdatableMultipartConfigElement extends MultipartConfigElement {
    
        private final long maxFileSize;
    
        public UpdatableMultipartConfigElement(String location, long maxFileSize, long maxRequestSize, int fileSizeThreshold) {
            super(location, maxFileSize, maxRequestSize, fileSizeThreshold);
            this.maxFileSize = maxFileSize;
        }
    
        @Override
        public long getMaxFileSize() {
            return MultipartConfigurationContext.getMaxFileSize() == null
                    ? maxFileSize : MultipartConfigurationContext.getMaxFileSize();
        }
    }
    
    public class UpdateMultipartConfigurationFilter extends OncePerRequestFilter implements Ordered {
    
        // this filter must be before OrderedHiddenHttpMethodFilter
        public static final int ORDER = FilterRegistrationBean.REQUEST_WRAPPER_FILTER_MAX_ORDER - 20000;
    
        @Override
        public int getOrder() {
            return ORDER;
        }
    
        @Override
        protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
                throws ServletException, IOException {
            // gets file size from request if present and sets it to MultipartConfigurationContext
            MultipartConfigurationContext.setMaxFileSize(150_000);
    
            filterChain.doFilter(request, response);
    
            MultipartConfigurationContext.clear();
        }
    }
    
    @Configuration
    @EnableConfigurationProperties(MultipartProperties.class)
    public class MultipartConfiguration {
    
        private final MultipartProperties multipartProperties;
    
        public MultipartConfiguration(MultipartProperties multipartProperties) {
            this.multipartProperties = multipartProperties;
        }
    
        @Bean
        public MultipartConfigElement multipartConfigElement() {
            MultipartConfigElement multipartConfigElement = multipartProperties.createMultipartConfig();
            return new UpdatableMultipartConfigElement(multipartConfigElement.getLocation(), multipartConfigElement.getMaxFileSize(),
                    multipartConfigElement.getMaxRequestSize(), multipartConfigElement.getFileSizeThreshold());
        }
    
        @Bean
        public UpdateMultipartConfigurationFilter updateMultipartConfigurationFilter() {
            return new UpdateMultipartConfigurationFilter();
        }
    }
    

    Globally

    UpdatableMultipartConfigElement stores information which can be updated in runtime using rest api call.

    public class UpdatableMultipartConfigElement extends MultipartConfigElement {
    
        private volatile long maxFileSize = -1;
    
        public UpdatableMultipartConfigElement(String location, long maxFileSize, long maxRequestSize, int fileSizeThreshold) {
            super(location, maxFileSize, maxRequestSize, fileSizeThreshold);
        }
    
        @Override
        public long getMaxFileSize() {
            return maxFileSize == -1 ? super.getMaxFileSize() : maxFileSize;
        }
    
        public void setMaxFileSize(long maxFileSize) {
            this.maxFileSize = maxFileSize;
        }
    }
    
    @Configuration
    @EnableConfigurationProperties(MultipartProperties.class)
    public class MultipartConfiguration {
    
        private final MultipartProperties multipartProperties;
    
        public MultipartConfiguration(MultipartProperties multipartProperties) {
            this.multipartProperties = multipartProperties;
        }
    
        @Bean
        public MultipartConfigElement multipartConfigElement() {
            MultipartConfigElement multipartConfigElement = multipartProperties.createMultipartConfig();
            return new UpdatableMultipartConfigElement(multipartConfigElement.getLocation(), multipartConfigElement.getMaxFileSize(),
                    multipartConfigElement.getMaxRequestSize(), multipartConfigElement.getFileSizeThreshold());
        }
    }
    
    @RestController
    public class ConfigurationController {
    
        @Autowired
        private UpdatableMultipartConfigElement updatableMultipartConfigElement;
    
        @RequestMapping("/configuration")
        public void configuration(@RequestParam("maxFileSize") long maxFileSize) {
            updatableMultipartConfigElement.setMaxFileSize(maxFileSize);
        }
    
    }