diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartAutoConfiguration.java index 93646b4802..cc949ad4be 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartAutoConfiguration.java @@ -31,6 +31,7 @@ import org.springframework.boot.web.codec.CodecCustomizer; import org.springframework.context.annotation.Bean; import org.springframework.core.annotation.Order; import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader; +import org.springframework.http.codec.multipart.PartEventHttpMessageReader; import org.springframework.util.unit.DataSize; import org.springframework.web.reactive.config.WebFluxConfigurer; @@ -61,11 +62,18 @@ public class ReactiveMultipartAutoConfiguration { map.from(multipartProperties::getMaxDiskUsagePerPart).asInt(DataSize::toBytes) .to(defaultPartHttpMessageReader::setMaxDiskUsagePerPart); map.from(multipartProperties::getMaxParts).to(defaultPartHttpMessageReader::setMaxParts); - map.from(multipartProperties::getStreaming).to(defaultPartHttpMessageReader::setStreaming); map.from(multipartProperties::getFileStorageDirectory).as(Paths::get) .to((dir) -> configureFileStorageDirectory(defaultPartHttpMessageReader, dir)); map.from(multipartProperties::getHeadersCharset).to(defaultPartHttpMessageReader::setHeadersCharset); } + else if (codec instanceof PartEventHttpMessageReader partEventHttpMessageReader) { + PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); + map.from(multipartProperties::getMaxInMemorySize).asInt(DataSize::toBytes) + .to(partEventHttpMessageReader::setMaxInMemorySize); + map.from(multipartProperties::getMaxHeadersSize).asInt(DataSize::toBytes) + .to(partEventHttpMessageReader::setMaxHeadersSize); + map.from(multipartProperties::getHeadersCharset).to(partEventHttpMessageReader::setHeadersCharset); + } }); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartProperties.java index 2bdb759f3f..b1bd6d7854 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,11 +21,13 @@ import java.nio.charset.StandardCharsets; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader; +import org.springframework.http.codec.multipart.PartEventHttpMessageReader; import org.springframework.util.unit.DataSize; /** * {@link ConfigurationProperties Configuration properties} for configuring multipart - * support in Spring Webflux. Used to configure the {@link DefaultPartHttpMessageReader}. + * support in Spring Webflux. Used to configure the {@link DefaultPartHttpMessageReader} + * and the {@link PartEventHttpMessageReader}. * * @author Chris Bono * @since 2.6.0 @@ -57,12 +59,6 @@ public class ReactiveMultipartProperties { */ private Integer maxParts = -1; - /** - * Whether to stream directly from the parsed input buffer stream without storing in - * memory nor file. Default is non-streaming. - */ - private Boolean streaming = Boolean.FALSE; - /** * Directory used to store file parts larger than 'maxInMemorySize'. Default is a * directory named 'spring-multipart' created under the system temporary directory. @@ -107,14 +103,6 @@ public class ReactiveMultipartProperties { this.maxParts = maxParts; } - public Boolean getStreaming() { - return this.streaming; - } - - public void setStreaming(Boolean streaming) { - this.streaming = streaming; - } - public String getFileStorageDirectory() { return this.fileStorageDirectory; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 37e215c30b..3492f36c35 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -3376,6 +3376,14 @@ "name": "any" } ] + }, + { + "name": "spring.webflux.multipart.streaming", + "type": "java.lang.Boolean", + "deprecation": { + "reason": "Replaced by the PartEventHttpMessageReader and the PartEvent API.", + "level": "error" + } } ] } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartAutoConfigurationTests.java index 82b3093582..4959eacc94 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartAutoConfigurationTests.java @@ -26,6 +26,7 @@ import org.springframework.boot.test.context.runner.ReactiveWebApplicationContex import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.boot.web.codec.CodecCustomizer; import org.springframework.http.codec.multipart.DefaultPartHttpMessageReader; +import org.springframework.http.codec.multipart.PartEventHttpMessageReader; import org.springframework.http.codec.support.DefaultServerCodecConfigurer; import org.springframework.util.unit.DataSize; import org.springframework.web.reactive.config.WebFluxConfigurer; @@ -36,6 +37,7 @@ import static org.assertj.core.api.Assertions.assertThat; * Tests for {@link ReactiveMultipartAutoConfiguration}. * * @author Chris Bono + * @author Brian Clozel */ class ReactiveMultipartAutoConfigurationTests { @@ -56,16 +58,17 @@ class ReactiveMultipartAutoConfigurationTests { } @Test - void shouldConfigureMultipartProperties() { - this.contextRunner.withPropertyValues("spring.webflux.multipart.streaming:true", - "spring.webflux.multipart.max-in-memory-size=1GB", "spring.webflux.multipart.max-headers-size=16KB", - "spring.webflux.multipart.max-disk-usage-per-part=100MB", "spring.webflux.multipart.max-parts=7", - "spring.webflux.multipart.headers-charset:UTF_16").run((context) -> { + void shouldConfigureMultipartPropertiesForDefaultReader() { + this.contextRunner + .withPropertyValues("spring.webflux.multipart.max-in-memory-size=1GB", + "spring.webflux.multipart.max-headers-size=16KB", + "spring.webflux.multipart.max-disk-usage-per-part=100MB", + "spring.webflux.multipart.max-parts=7", "spring.webflux.multipart.headers-charset:UTF_16") + .run((context) -> { CodecCustomizer customizer = context.getBean(CodecCustomizer.class); DefaultServerCodecConfigurer configurer = new DefaultServerCodecConfigurer(); customizer.customize(configurer); - DefaultPartHttpMessageReader partReader = getPartReader(configurer); - assertThat(partReader).hasFieldOrPropertyWithValue("streaming", true); + DefaultPartHttpMessageReader partReader = getDefaultPartReader(configurer); assertThat(partReader).hasFieldOrPropertyWithValue("maxParts", 7); assertThat(partReader).hasFieldOrPropertyWithValue("maxHeadersSize", Math.toIntExact(DataSize.ofKilobytes(16).toBytes())); @@ -77,10 +80,33 @@ class ReactiveMultipartAutoConfigurationTests { }); } - private DefaultPartHttpMessageReader getPartReader(DefaultServerCodecConfigurer codecConfigurer) { + @Test + void shouldConfigureMultipartPropertiesForPartEventReader() { + this.contextRunner.withPropertyValues("spring.webflux.multipart.max-in-memory-size=1GB", + "spring.webflux.multipart.max-headers-size=16KB", "spring.webflux.multipart.headers-charset:UTF_16") + .run((context) -> { + CodecCustomizer customizer = context.getBean(CodecCustomizer.class); + DefaultServerCodecConfigurer configurer = new DefaultServerCodecConfigurer(); + customizer.customize(configurer); + PartEventHttpMessageReader partReader = getPartEventReader(configurer); + assertThat(partReader).hasFieldOrPropertyWithValue("maxHeadersSize", + Math.toIntExact(DataSize.ofKilobytes(16).toBytes())); + assertThat(partReader).hasFieldOrPropertyWithValue("headersCharset", StandardCharsets.UTF_16); + assertThat(partReader).hasFieldOrPropertyWithValue("maxInMemorySize", + Math.toIntExact(DataSize.ofGigabytes(1).toBytes())); + }); + } + + private DefaultPartHttpMessageReader getDefaultPartReader(DefaultServerCodecConfigurer codecConfigurer) { return codecConfigurer.getReaders().stream().filter(DefaultPartHttpMessageReader.class::isInstance) .map(DefaultPartHttpMessageReader.class::cast).findFirst() .orElseThrow(() -> new IllegalStateException("Could not find DefaultPartHttpMessageReader")); } + private PartEventHttpMessageReader getPartEventReader(DefaultServerCodecConfigurer codecConfigurer) { + return codecConfigurer.getReaders().stream().filter(PartEventHttpMessageReader.class::isInstance) + .map(PartEventHttpMessageReader.class::cast).findFirst() + .orElseThrow(() -> new IllegalStateException("Could not find PartEventHttpMessageReader")); + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartPropertiesTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartPropertiesTests.java index ea0b72ebfb..5e0662e246 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartPropertiesTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/web/reactive/ReactiveMultipartPropertiesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2021 the original author or authors. + * Copyright 2012-2022 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,8 +33,6 @@ class ReactiveMultipartPropertiesTests { void defaultValuesAreConsistent() { ReactiveMultipartProperties multipartProperties = new ReactiveMultipartProperties(); DefaultPartHttpMessageReader defaultPartHttpMessageReader = new DefaultPartHttpMessageReader(); - assertThat(defaultPartHttpMessageReader).hasFieldOrPropertyWithValue("streaming", - multipartProperties.getStreaming()); assertThat(defaultPartHttpMessageReader).hasFieldOrPropertyWithValue("maxInMemorySize", (int) multipartProperties.getMaxInMemorySize().toBytes()); assertThat(defaultPartHttpMessageReader).hasFieldOrPropertyWithValue("maxHeadersSize",