Automatically clean up multipart temp files

This commit ensures that any resources created for multipart handling,
obtained via ServerWebExchange.getMultipartData(), are automatically
deleted after handling the completing the response.

Resource for parts obtained via BodyExtractors::toMultipartData and
BodyExtractors::toParts are not cleaned automatically, and
should be cleaned via Part::delete.

Closes gh-27633
This commit is contained in:
Arjen Poutsma
2022-03-29 17:01:34 +02:00
parent f52920142b
commit 192f2becf6
3 changed files with 36 additions and 2 deletions

View File

@@ -18,7 +18,9 @@ package org.springframework.web.server.adapter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@@ -32,6 +34,7 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.codec.LoggingCodecSupport;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.codec.multipart.Part;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
@@ -249,6 +252,7 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
return getDelegate().handle(exchange)
.doOnSuccess(aVoid -> logResponse(exchange))
.onErrorResume(ex -> handleUnresolvedError(exchange, ex))
.then(cleanupMultipart(exchange))
.then(Mono.defer(response::setComplete));
}
@@ -323,4 +327,23 @@ public class HttpWebHandlerAdapter extends WebHandlerDecorator implements HttpHa
return DISCONNECTED_CLIENT_EXCEPTIONS.contains(ex.getClass().getSimpleName());
}
private Mono<Void> cleanupMultipart(ServerWebExchange exchange) {
return exchange.getMultipartData()
.onErrorResume(t -> Mono.empty()) // ignore errors reading multipart data
.flatMapIterable(Map::values)
.flatMapIterable(Function.identity())
.flatMap(this::deletePart)
.then();
}
private Mono<Void> deletePart(Part part) {
return part.delete().onErrorResume(ex -> {
if (logger.isWarnEnabled()) {
logger.warn("Failed to perform cleanup of multipart items", ex);
}
return Mono.empty();
});
}
}