Skip to content

Commit aa8b06b

Browse files
committed
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
1 parent 323dbb9 commit aa8b06b

File tree

2 files changed

+36
-1
lines changed

2 files changed

+36
-1
lines changed

spring-web/src/main/java/org/springframework/http/codec/multipart/MultipartUtils.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2022 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -20,6 +20,8 @@
2020
import java.nio.channels.Channel;
2121
import java.nio.charset.Charset;
2222
import java.nio.charset.StandardCharsets;
23+
import java.nio.file.Files;
24+
import java.nio.file.Path;
2325

2426
import org.springframework.core.io.buffer.DataBuffer;
2527
import org.springframework.http.HttpHeaders;
@@ -91,4 +93,12 @@ public static void closeChannel(Channel channel) {
9193
}
9294
}
9395

96+
public static void deleteFile(Path file) {
97+
try {
98+
Files.delete(file);
99+
}
100+
catch (IOException ignore) {
101+
}
102+
}
103+
94104
}

spring-web/src/main/java/org/springframework/http/codec/multipart/PartGenerator.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,7 @@ private void fileCreated(WritingFileState newState) {
609609
}
610610
else {
611611
MultipartUtils.closeChannel(newState.channel);
612+
MultipartUtils.deleteFile(newState.file);
612613
this.content.forEach(DataBufferUtils::release);
613614
}
614615
}
@@ -640,6 +641,8 @@ private final class IdleFileState implements State {
640641

641642
private volatile boolean closeOnDispose = true;
642643

644+
private volatile boolean deleteOnDispose = true;
645+
643646

644647
public IdleFileState(WritingFileState state) {
645648
this.headers = state.headers;
@@ -654,16 +657,20 @@ public void body(DataBuffer dataBuffer) {
654657
if (PartGenerator.this.maxDiskUsagePerPart == -1 || count <= PartGenerator.this.maxDiskUsagePerPart) {
655658

656659
this.closeOnDispose = false;
660+
this.deleteOnDispose = false;
657661
WritingFileState newState = new WritingFileState(this);
658662
if (changeState(this, newState)) {
659663
newState.writeBuffer(dataBuffer);
660664
}
661665
else {
662666
MultipartUtils.closeChannel(this.channel);
667+
MultipartUtils.deleteFile(this.file);
663668
DataBufferUtils.release(dataBuffer);
664669
}
665670
}
666671
else {
672+
MultipartUtils.closeChannel(this.channel);
673+
MultipartUtils.deleteFile(this.file);
667674
DataBufferUtils.release(dataBuffer);
668675
emitError(new DataBufferLimitException(
669676
"Part exceeded the disk usage limit of " + PartGenerator.this.maxDiskUsagePerPart +
@@ -674,6 +681,7 @@ public void body(DataBuffer dataBuffer) {
674681
@Override
675682
public void partComplete(boolean finalPart) {
676683
MultipartUtils.closeChannel(this.channel);
684+
this.deleteOnDispose = false;
677685
emitPart(DefaultParts.part(this.headers, this.file, PartGenerator.this.blockingOperationScheduler));
678686
if (finalPart) {
679687
emitComplete();
@@ -685,6 +693,9 @@ public void dispose() {
685693
if (this.closeOnDispose) {
686694
MultipartUtils.closeChannel(this.channel);
687695
}
696+
if (this.deleteOnDispose) {
697+
MultipartUtils.deleteFile(this.file);
698+
}
688699
}
689700

690701

@@ -710,6 +721,8 @@ private final class WritingFileState implements State {
710721

711722
private volatile boolean finalPart;
712723

724+
private volatile boolean disposed;
725+
713726

714727
public WritingFileState(CreateFileState state, Path file, WritableByteChannel channel) {
715728
this.headers = state.headers;
@@ -761,11 +774,15 @@ private void writeComplete() {
761774
if (this.completed) {
762775
newState.partComplete(this.finalPart);
763776
}
777+
else if (this.disposed) {
778+
newState.dispose();
779+
}
764780
else if (changeState(this, newState)) {
765781
requestToken();
766782
}
767783
else {
768784
MultipartUtils.closeChannel(this.channel);
785+
MultipartUtils.deleteFile(this.file);
769786
}
770787
}
771788

@@ -779,13 +796,21 @@ private Mono<Void> writeInternal(DataBuffer dataBuffer) {
779796
return Mono.empty();
780797
}
781798
catch (IOException ex) {
799+
MultipartUtils.closeChannel(this.channel);
800+
MultipartUtils.deleteFile(this.file);
782801
return Mono.error(ex);
783802
}
784803
finally {
785804
DataBufferUtils.release(dataBuffer);
786805
}
787806
}
788807

808+
@Override
809+
public void dispose() {
810+
this.disposed = true;
811+
}
812+
813+
789814
@Override
790815
public String toString() {
791816
return "WRITE-FILE";

0 commit comments

Comments
 (0)