Skip to content

Commit c6a61e0

Browse files
poutsmarstoyanchev
authored andcommitted
Add Resource.readableChannel()
Added readableChannel() to Resource, which returns a java.nio.ReadableByteChannel. The default implementation uses Channels.newChannel() to create a channel based on what is returned from getInputStream(). Subclasses have more effecient, file-based implementations. Issue: SPR-14698
1 parent 06395f4 commit c6a61e0

File tree

8 files changed

+126
-4
lines changed

8 files changed

+126
-4
lines changed

spring-core/src/main/java/org/springframework/core/codec/ResourceEncoder.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
package org.springframework.core.codec;
1818

1919
import java.io.IOException;
20-
import java.io.InputStream;
20+
import java.nio.channels.ReadableByteChannel;
2121
import java.util.Map;
2222

2323
import reactor.core.publisher.Flux;
@@ -66,8 +66,8 @@ public boolean canEncode(ResolvableType elementType, MimeType mimeType, Map<Stri
6666
protected Flux<DataBuffer> encode(Resource resource, DataBufferFactory dataBufferFactory,
6767
ResolvableType type, MimeType mimeType, Map<String, Object> hints) throws IOException {
6868

69-
InputStream is = resource.getInputStream();
70-
return DataBufferUtils.read(is, dataBufferFactory, bufferSize);
69+
ReadableByteChannel channel = resource.readableChannel();
70+
return DataBufferUtils.read(channel, dataBufferFactory, bufferSize);
7171
}
7272

7373
}

spring-core/src/main/java/org/springframework/core/io/AbstractFileResolvingResource.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,14 @@
1717
package org.springframework.core.io;
1818

1919
import java.io.File;
20+
import java.io.FileInputStream;
2021
import java.io.IOException;
2122
import java.io.InputStream;
2223
import java.net.HttpURLConnection;
2324
import java.net.URI;
2425
import java.net.URL;
2526
import java.net.URLConnection;
27+
import java.nio.channels.ReadableByteChannel;
2628

2729
import org.springframework.util.ResourceUtils;
2830

@@ -115,6 +117,22 @@ protected File getFile(URI uri) throws IOException {
115117
return ResourceUtils.getFile(uri, getDescription());
116118
}
117119

120+
/**
121+
* This implementation returns a FileChannel for the given URI-identified
122+
* resource, provided that it refers to a file in the file system.
123+
* @since 5.0
124+
* @see #getFile(URI)
125+
*/
126+
@Override
127+
public ReadableByteChannel readableChannel() throws IOException {
128+
if (isFile()) {
129+
return new FileInputStream(getFile()).getChannel();
130+
}
131+
else {
132+
return super.readableChannel();
133+
}
134+
}
135+
118136

119137
@Override
120138
public boolean exists() {

spring-core/src/main/java/org/springframework/core/io/AbstractResource.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import java.net.URI;
2424
import java.net.URISyntaxException;
2525
import java.net.URL;
26+
import java.nio.channels.Channels;
27+
import java.nio.channels.ReadableByteChannel;
2628

2729
import org.springframework.core.NestedIOException;
2830
import org.springframework.util.Assert;
@@ -122,6 +124,15 @@ public File getFile() throws IOException {
122124
throw new FileNotFoundException(getDescription() + " cannot be resolved to absolute file path");
123125
}
124126

127+
/**
128+
* This implementation returns {@link Channels#newChannel(InputStream)} with the result of
129+
* {@link #getInputStream()}.
130+
*/
131+
@Override
132+
public ReadableByteChannel readableChannel() throws IOException {
133+
return Channels.newChannel(getInputStream());
134+
}
135+
125136
/**
126137
* This implementation reads the entire InputStream to calculate the
127138
* content length. Subclasses will almost always be able to provide

spring-core/src/main/java/org/springframework/core/io/FileSystemResource.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.io.OutputStream;
2525
import java.net.URI;
2626
import java.net.URL;
27+
import java.nio.channels.ReadableByteChannel;
2728

2829
import org.springframework.util.Assert;
2930
import org.springframework.util.StringUtils;
@@ -168,6 +169,15 @@ public File getFile() {
168169
return this.file;
169170
}
170171

172+
/**
173+
* This implementation opens a FileChannel for the underlying file.
174+
* @see java.nio.channels.FileChannel
175+
*/
176+
@Override
177+
public ReadableByteChannel readableChannel() throws IOException {
178+
return new FileInputStream(this.file).getChannel();
179+
}
180+
171181
/**
172182
* This implementation returns the underlying File's length.
173183
*/

spring-core/src/main/java/org/springframework/core/io/PathResource.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@
2323
import java.io.OutputStream;
2424
import java.net.URI;
2525
import java.net.URL;
26+
import java.nio.channels.ReadableByteChannel;
2627
import java.nio.file.Files;
2728
import java.nio.file.OpenOption;
2829
import java.nio.file.Path;
2930
import java.nio.file.Paths;
31+
import java.nio.file.StandardOpenOption;
3032

3133
import org.springframework.util.Assert;
3234

@@ -194,6 +196,15 @@ public File getFile() throws IOException {
194196
}
195197
}
196198

199+
/**
200+
* This implementation opens a InputStream for the underlying file.
201+
* @see java.nio.file.spi.FileSystemProvider#newInputStream(Path, OpenOption...)
202+
*/
203+
@Override
204+
public ReadableByteChannel readableChannel() throws IOException {
205+
return Files.newByteChannel(this.path, StandardOpenOption.READ);
206+
}
207+
197208
/**
198209
* This implementation returns the underlying File's length.
199210
*/

spring-core/src/main/java/org/springframework/core/io/Resource.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,11 @@
1818

1919
import java.io.File;
2020
import java.io.IOException;
21+
import java.io.InputStream;
2122
import java.net.URI;
2223
import java.net.URL;
24+
import java.nio.channels.Channels;
25+
import java.nio.channels.ReadableByteChannel;
2326

2427
/**
2528
* Interface for a resource descriptor that abstracts from the actual
@@ -111,6 +114,19 @@ default boolean isFile() {
111114
*/
112115
File getFile() throws IOException;
113116

117+
/**
118+
* Return a {@link ReadableByteChannel}.
119+
* <p>It is expected that each call creates a <i>fresh</i> channel.
120+
* <p>The default implementation returns {@link Channels#newChannel(InputStream)} with the
121+
* result of {@link #getInputStream()}.
122+
* @return the byte channel for the underlying resource (must not be {@code null})
123+
* @throws IOException if the channel could not be opened
124+
* @since 5.0
125+
*/
126+
default ReadableByteChannel readableChannel() throws IOException {
127+
return Channels.newChannel(getInputStream());
128+
}
129+
114130
/**
115131
* Determine the content length for this resource.
116132
* @throws IOException if the resource cannot be resolved

spring-core/src/test/java/org/springframework/core/io/PathResourceTests.java

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2015 the original author or authors.
2+
* Copyright 2002-2016 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.
@@ -19,6 +19,9 @@
1919
import java.io.File;
2020
import java.io.FileNotFoundException;
2121
import java.net.URI;
22+
import java.nio.ByteBuffer;
23+
import java.nio.channels.ReadableByteChannel;
24+
import java.nio.file.NoSuchFileException;
2225
import java.nio.file.Path;
2326
import java.nio.file.Paths;
2427

@@ -42,6 +45,7 @@
4245
* @author Nicholas Williams
4346
* @author Stephane Nicoll
4447
* @author Juergen Hoeller
48+
* @author Arjen Poutsma
4549
*/
4650
public class PathResourceTests {
4751

@@ -287,4 +291,36 @@ public void directoryOutputStream() throws Exception {
287291
resource.getOutputStream();
288292
}
289293

294+
@Test
295+
public void getReadableByteChannel() throws Exception {
296+
PathResource resource = new PathResource(TEST_FILE);
297+
ReadableByteChannel channel = null;
298+
try {
299+
channel = resource.readableChannel();
300+
ByteBuffer buffer = ByteBuffer.allocate((int) resource.contentLength());
301+
channel.read(buffer);
302+
buffer.rewind();
303+
assertThat(buffer.limit(), greaterThan(0));
304+
}
305+
finally {
306+
if (channel != null) {
307+
channel.close();
308+
}
309+
}
310+
}
311+
312+
@Test
313+
public void getReadableByteChannelForDir() throws Exception {
314+
PathResource resource = new PathResource(TEST_DIR);
315+
thrown.expect(NoSuchFileException.class);
316+
resource.readableChannel();
317+
}
318+
319+
@Test
320+
public void getReadableByteChannelDoesNotExist() throws Exception {
321+
PathResource resource = new PathResource(NON_EXISTING_FILE);
322+
thrown.expect(NoSuchFileException.class);
323+
resource.readableChannel();
324+
}
325+
290326
}

spring-core/src/test/java/org/springframework/core/io/ResourceTests.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121
import java.io.IOException;
2222
import java.io.InputStream;
2323
import java.io.InputStreamReader;
24+
import java.nio.ByteBuffer;
25+
import java.nio.channels.ReadableByteChannel;
2426
import java.util.HashSet;
2527

2628
import org.junit.Ignore;
@@ -254,4 +256,22 @@ public String getDescription() {
254256
resource.contentLength();
255257
}
256258

259+
@Test
260+
public void testGetReadableByteChannel() throws IOException {
261+
Resource resource = new FileSystemResource(getClass().getResource("Resource.class").getFile());
262+
ReadableByteChannel channel = null;
263+
try {
264+
channel = resource.readableChannel();
265+
ByteBuffer buffer = ByteBuffer.allocate((int) resource.contentLength());
266+
channel.read(buffer);
267+
buffer.rewind();
268+
assertTrue(buffer.limit() > 0);
269+
}
270+
finally {
271+
if (channel != null) {
272+
channel.close();
273+
}
274+
}
275+
}
276+
257277
}

0 commit comments

Comments
 (0)