Delete file on multipart cancel

This commit makes sure that the temporary file used for multipart
storage is deleted when the source stream is malformed or cancelled.

Closes gh-28740
This commit is contained in:
Arjen Poutsma
2022-07-04 14:58:00 +02:00
parent 323dbb912e
commit aa8b06b622
2 changed files with 36 additions and 1 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-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.
@@ -20,6 +20,8 @@ import java.io.IOException;
import java.nio.channels.Channel;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
@@ -91,4 +93,12 @@ abstract class MultipartUtils {
}
}
public static void deleteFile(Path file) {
try {
Files.delete(file);
}
catch (IOException ignore) {
}
}
}

View File

@@ -609,6 +609,7 @@ final class PartGenerator extends BaseSubscriber<MultipartParser.Token> {
}
else {
MultipartUtils.closeChannel(newState.channel);
MultipartUtils.deleteFile(newState.file);
this.content.forEach(DataBufferUtils::release);
}
}
@@ -640,6 +641,8 @@ final class PartGenerator extends BaseSubscriber<MultipartParser.Token> {
private volatile boolean closeOnDispose = true;
private volatile boolean deleteOnDispose = true;
public IdleFileState(WritingFileState state) {
this.headers = state.headers;
@@ -654,16 +657,20 @@ final class PartGenerator extends BaseSubscriber<MultipartParser.Token> {
if (PartGenerator.this.maxDiskUsagePerPart == -1 || count <= PartGenerator.this.maxDiskUsagePerPart) {
this.closeOnDispose = false;
this.deleteOnDispose = false;
WritingFileState newState = new WritingFileState(this);
if (changeState(this, newState)) {
newState.writeBuffer(dataBuffer);
}
else {
MultipartUtils.closeChannel(this.channel);
MultipartUtils.deleteFile(this.file);
DataBufferUtils.release(dataBuffer);
}
}
else {
MultipartUtils.closeChannel(this.channel);
MultipartUtils.deleteFile(this.file);
DataBufferUtils.release(dataBuffer);
emitError(new DataBufferLimitException(
"Part exceeded the disk usage limit of " + PartGenerator.this.maxDiskUsagePerPart +
@@ -674,6 +681,7 @@ final class PartGenerator extends BaseSubscriber<MultipartParser.Token> {
@Override
public void partComplete(boolean finalPart) {
MultipartUtils.closeChannel(this.channel);
this.deleteOnDispose = false;
emitPart(DefaultParts.part(this.headers, this.file, PartGenerator.this.blockingOperationScheduler));
if (finalPart) {
emitComplete();
@@ -685,6 +693,9 @@ final class PartGenerator extends BaseSubscriber<MultipartParser.Token> {
if (this.closeOnDispose) {
MultipartUtils.closeChannel(this.channel);
}
if (this.deleteOnDispose) {
MultipartUtils.deleteFile(this.file);
}
}
@@ -710,6 +721,8 @@ final class PartGenerator extends BaseSubscriber<MultipartParser.Token> {
private volatile boolean finalPart;
private volatile boolean disposed;
public WritingFileState(CreateFileState state, Path file, WritableByteChannel channel) {
this.headers = state.headers;
@@ -761,11 +774,15 @@ final class PartGenerator extends BaseSubscriber<MultipartParser.Token> {
if (this.completed) {
newState.partComplete(this.finalPart);
}
else if (this.disposed) {
newState.dispose();
}
else if (changeState(this, newState)) {
requestToken();
}
else {
MultipartUtils.closeChannel(this.channel);
MultipartUtils.deleteFile(this.file);
}
}
@@ -779,6 +796,8 @@ final class PartGenerator extends BaseSubscriber<MultipartParser.Token> {
return Mono.empty();
}
catch (IOException ex) {
MultipartUtils.closeChannel(this.channel);
MultipartUtils.deleteFile(this.file);
return Mono.error(ex);
}
finally {
@@ -786,6 +805,12 @@ final class PartGenerator extends BaseSubscriber<MultipartParser.Token> {
}
}
@Override
public void dispose() {
this.disposed = true;
}
@Override
public String toString() {
return "WRITE-FILE";