Multipart file missing with Spring Gateway MVC

After routing multipart/form-data through gateway mvc, downstream service receives the request without multipart file

This issue is present in org.springframework.cloud:spring-cloud-gateway-mvc:4.2.0

Solution

The multipart data is actually received by the gateway, but when the gateway consumes it it's not available anymore so it's not sent to the downstream service. To avoid this from happening, you can disable multipart in spring properties

spring.servlet.multipart.enabled=false

This should be done only in gateway properties, downstream services need to enable multipart instead

Doing it prevents the gateway to process the multipart data, and will make it available to downstream services.

This solution is documented more in details here

Alternative #1

I had this exact issue last year and the spring.servlet.multipart.enabled=false fix worked, but I also found another approach that might be worth considering.

You can configure the gateway to preserve the multipart data by setting specific multipart properties:

spring:
  servlet:
    multipart:
      enabled: true
      max-file-size: 10MB
      max-request-size: 10MB
      resolve-lazily: true

The key here is resolve-lazily: true - this prevents the gateway from eagerly parsing the multipart data, so it gets passed through to your downstream service intact.

This approach keeps multipart enabled (which might be needed for other parts of your gateway) while still solving the file transfer issue.

Alternative #2

We ran into this in production and ended up using a custom filter approach. The problem was that our gateway needed to handle both multipart and regular requests, so disabling multipart entirely wasn't an option.

Here's what we implemented:

@Component
public class MultipartPreservingFilter implements GlobalFilter {
    
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        
        if (request.getHeaders().getContentType() != null && 
            request.getHeaders().getContentType().includes(MediaType.MULTIPART_FORM_DATA)) {
            
            // Skip multipart processing in gateway
            return chain.filter(exchange.mutate()
                .request(request.mutate()
                    .header("X-Skip-Multipart-Processing", "true")
                    .build())
                .build());
        }
        
        return chain.filter(exchange);
    }
}

This filter adds a custom header that tells your downstream service to handle the multipart processing. It's a bit more complex but gives you fine-grained control.

Alternative #3

Just wanted to add that this is actually a known limitation of Spring Cloud Gateway MVC. We switched to using Spring Cloud Gateway (the reactive version) instead, which handles multipart data much better.

If you're starting a new project or can migrate, consider using:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

The reactive gateway doesn't have this multipart consumption issue and generally performs better for high-throughput scenarios. The migration isn't trivial, but it's worth considering if you're dealing with a lot of file uploads.

Plus, you won't have to deal with these workarounds anymore.

Last modified: January 25, 2025