Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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 ?

like image 496
gstackoverflow Avatar asked Jul 15 '26 06:07

gstackoverflow


1 Answers

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);
    }

}
like image 69
Nikita Gorbachevski Avatar answered Jul 17 '26 18:07

Nikita Gorbachevski