Skip to content

Commit f7d60b7

Browse files
committed
Add getResource to MultipartFile
Issue: SPR-16808
1 parent ab6a6f4 commit f7d60b7

File tree

3 files changed

+153
-11
lines changed

3 files changed

+153
-11
lines changed

spring-web/src/main/java/org/springframework/web/multipart/MultipartFile.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 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.
@@ -21,6 +21,7 @@
2121
import java.io.InputStream;
2222

2323
import org.springframework.core.io.InputStreamSource;
24+
import org.springframework.core.io.Resource;
2425
import org.springframework.lang.Nullable;
2526

2627
/**
@@ -93,6 +94,17 @@ public interface MultipartFile extends InputStreamSource {
9394
@Override
9495
InputStream getInputStream() throws IOException;
9596

97+
/**
98+
* Return a Resource representation of this MultipartFile. This can be used
99+
* as input to the {@code RestTemplate} or the {@code WebClient} to expose
100+
* content length and the filename along with the InputStream.
101+
* @return this MultipartFile adapted to the Resource contract
102+
* @since 5.1
103+
*/
104+
default Resource getResource() {
105+
return new MultipartFileResource(this);
106+
}
107+
96108
/**
97109
* Transfer the received file to the given destination file.
98110
* <p>This may either move the file in the filesystem, copy the file in the
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright 2002-2018 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.web.multipart;
17+
18+
import java.io.IOException;
19+
import java.io.InputStream;
20+
21+
import org.springframework.core.io.AbstractResource;
22+
import org.springframework.util.Assert;
23+
24+
/**
25+
* Adapt {@link MultipartFile} to {@link org.springframework.core.io.Resource},
26+
* exposing the content as {@code InputStream} and also overriding
27+
* {@link #contentLength()} as well as {@link #getFilename()}.
28+
*
29+
* @author Rossen Stoyanchev
30+
* @since 5.1
31+
*/
32+
class MultipartFileResource extends AbstractResource {
33+
34+
private final MultipartFile multipartFile;
35+
36+
37+
public MultipartFileResource(MultipartFile multipartFile) {
38+
Assert.notNull(multipartFile, "MultipartFile must not be null.");
39+
this.multipartFile = multipartFile;
40+
}
41+
42+
43+
/**
44+
* This implementation always returns {@code true}.
45+
*/
46+
@Override
47+
public boolean exists() {
48+
return true;
49+
}
50+
51+
/**
52+
* This implementation always returns {@code true}.
53+
*/
54+
@Override
55+
public boolean isOpen() {
56+
return true;
57+
}
58+
59+
@Override
60+
public long contentLength() {
61+
return this.multipartFile.getSize();
62+
}
63+
64+
@Override
65+
public String getFilename() {
66+
return this.multipartFile.getOriginalFilename();
67+
}
68+
69+
/**
70+
* This implementation throws IllegalStateException if attempting to
71+
* read the underlying stream multiple times.
72+
*/
73+
@Override
74+
public InputStream getInputStream() throws IOException, IllegalStateException {
75+
return this.multipartFile.getInputStream();
76+
}
77+
78+
/**
79+
* This implementation returns a description that has the Multipart name.
80+
*/
81+
@Override
82+
public String getDescription() {
83+
return "MultipartFile resource [" + this.multipartFile.getName() + "]";
84+
}
85+
86+
87+
@Override
88+
public boolean equals(Object obj) {
89+
return (obj == this ||
90+
(obj instanceof MultipartFileResource &&
91+
((MultipartFileResource) obj).multipartFile.equals(this.multipartFile)));
92+
}
93+
94+
@Override
95+
public int hashCode() {
96+
return this.multipartFile.hashCode();
97+
}
98+
99+
}

spring-web/src/test/java/org/springframework/web/multipart/support/StandardMultipartHttpServletRequestTests.java

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2017 the original author or authors.
2+
* Copyright 2002-2018 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.
@@ -16,12 +16,20 @@
1616

1717
package org.springframework.web.multipart.support;
1818

19+
import java.io.IOException;
20+
import java.nio.charset.StandardCharsets;
21+
1922
import org.junit.Test;
2023

24+
import org.springframework.http.MockHttpOutputMessage;
25+
import org.springframework.http.converter.FormHttpMessageConverter;
2126
import org.springframework.mock.web.test.MockHttpServletRequest;
2227
import org.springframework.mock.web.test.MockPart;
28+
import org.springframework.util.LinkedMultiValueMap;
29+
import org.springframework.util.MultiValueMap;
2330
import org.springframework.web.multipart.MultipartFile;
2431

32+
import static org.hamcrest.CoreMatchers.*;
2533
import static org.junit.Assert.*;
2634

2735
/**
@@ -33,8 +41,8 @@ public class StandardMultipartHttpServletRequestTests {
3341

3442
@Test
3543
public void filename() throws Exception {
36-
StandardMultipartHttpServletRequest request = getRequest(
37-
"file", "form-data; name=\"file\"; filename=\"myFile.txt\"");
44+
String disposition = "form-data; name=\"file\"; filename=\"myFile.txt\"";
45+
StandardMultipartHttpServletRequest request = requestWithPart("file", disposition, "");
3846

3947
MultipartFile multipartFile = request.getFile("file");
4048
assertNotNull(multipartFile);
@@ -43,8 +51,8 @@ public void filename() throws Exception {
4351

4452
@Test // SPR-13319
4553
public void filenameRfc5987() throws Exception {
46-
StandardMultipartHttpServletRequest request = getRequest(
47-
"file", "form-data; name=\"file\"; filename*=\"UTF-8''foo-%c3%a4-%e2%82%ac.html\"");
54+
String disposition = "form-data; name=\"file\"; filename*=\"UTF-8''foo-%c3%a4-%e2%82%ac.html\"";
55+
StandardMultipartHttpServletRequest request = requestWithPart("file", disposition, "");
4856

4957
MultipartFile multipartFile = request.getFile("file");
5058
assertNotNull(multipartFile);
@@ -53,19 +61,42 @@ public void filenameRfc5987() throws Exception {
5361

5462
@Test // SPR-15205
5563
public void filenameRfc2047() throws Exception {
56-
StandardMultipartHttpServletRequest request = getRequest(
57-
"file", "form-data; name=\"file\"; filename=\"=?UTF-8?Q?Declara=C3=A7=C3=A3o.pdf?=\"");
64+
String disposition = "form-data; name=\"file\"; filename=\"=?UTF-8?Q?Declara=C3=A7=C3=A3o.pdf?=\"";
65+
StandardMultipartHttpServletRequest request = requestWithPart("file", disposition, "");
5866

5967
MultipartFile multipartFile = request.getFile("file");
6068
assertNotNull(multipartFile);
6169
assertEquals("Declaração.pdf", multipartFile.getOriginalFilename());
6270
}
6371

72+
@Test
73+
public void multipartFileResource() throws IOException {
74+
String name = "file";
75+
String disposition = "form-data; name=\"" + name + "\"; filename=\"myFile.txt\"";
76+
StandardMultipartHttpServletRequest request = requestWithPart(name, disposition, "myBody");
77+
MultipartFile multipartFile = request.getFile(name);
78+
79+
assertNotNull(multipartFile);
80+
81+
MultiValueMap<String, Object> map = new LinkedMultiValueMap<>();
82+
map.add(name, multipartFile.getResource());
83+
84+
MockHttpOutputMessage output = new MockHttpOutputMessage();
85+
new FormHttpMessageConverter().write(map, null, output);
86+
87+
assertThat(output.getBodyAsString(StandardCharsets.UTF_8), containsString(
88+
"Content-Disposition: form-data; name=\"file\"; filename=\"myFile.txt\"\r\n" +
89+
"Content-Type: text/plain\r\n" +
90+
"Content-Length: 6\r\n" +
91+
"\r\n" +
92+
"myBody\r\n"));
93+
}
94+
6495

65-
private StandardMultipartHttpServletRequest getRequest(String name, String dispositionValue) {
96+
private StandardMultipartHttpServletRequest requestWithPart(String name, String disposition, String content) {
6697
MockHttpServletRequest request = new MockHttpServletRequest();
67-
MockPart part = new MockPart(name, null);
68-
part.getHeaders().set("Content-Disposition", dispositionValue);
98+
MockPart part = new MockPart(name, null, content.getBytes(StandardCharsets.UTF_8));
99+
part.getHeaders().set("Content-Disposition", disposition);
69100
request.addPart(part);
70101
return new StandardMultipartHttpServletRequest(request);
71102
}

0 commit comments

Comments
 (0)