Skip to content

Commit d56fedc

Browse files
committed
Methods for reading a Resource in DataBufferUtils
Currently ResourceEncoder and ResourceRegionEncoder use DataBufferUtils to read resource with an AsynchronousFileChannel if it is a file or otherwise fallback on getting the channel from the resource. The same is now required in other places where a Resource needs to be read and is also generally useful. Issue: SPR-15773
1 parent ee91e52 commit d56fedc

File tree

4 files changed

+95
-53
lines changed

4 files changed

+95
-53
lines changed

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

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,6 @@
1616

1717
package org.springframework.core.codec;
1818

19-
import java.io.File;
20-
import java.io.IOException;
21-
import java.nio.channels.AsynchronousFileChannel;
22-
import java.nio.channels.ReadableByteChannel;
23-
import java.nio.file.StandardOpenOption;
2419
import java.util.Map;
2520

2621
import reactor.core.publisher.Flux;
@@ -70,25 +65,7 @@ public boolean canEncode(ResolvableType elementType, @Nullable MimeType mimeType
7065
protected Flux<DataBuffer> encode(Resource resource, DataBufferFactory dataBufferFactory,
7166
ResolvableType type, @Nullable MimeType mimeType, @Nullable Map<String, Object> hints) {
7267

73-
try {
74-
if (resource.isFile()) {
75-
File file = resource.getFile();
76-
AsynchronousFileChannel channel =
77-
AsynchronousFileChannel.open(file.toPath(), StandardOpenOption.READ);
78-
return DataBufferUtils.read(channel, dataBufferFactory, this.bufferSize);
79-
}
80-
}
81-
catch (IOException ignore) {
82-
// fallback to resource.readableChannel(), below
83-
}
84-
85-
try {
86-
ReadableByteChannel channel = resource.readableChannel();
87-
return DataBufferUtils.read(channel, dataBufferFactory, this.bufferSize);
88-
}
89-
catch (IOException ex) {
90-
return Flux.error(ex);
91-
}
68+
return DataBufferUtils.read(resource, dataBufferFactory, this.bufferSize);
9269
}
9370

9471
}

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

Lines changed: 3 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,9 @@
1616

1717
package org.springframework.core.codec;
1818

19-
import java.io.File;
2019
import java.io.IOException;
2120
import java.nio.ByteBuffer;
22-
import java.nio.channels.AsynchronousFileChannel;
23-
import java.nio.channels.ReadableByteChannel;
2421
import java.nio.charset.StandardCharsets;
25-
import java.nio.file.StandardOpenOption;
2622
import java.util.Map;
2723
import java.util.OptionalLong;
2824

@@ -118,32 +114,10 @@ private Flux<DataBuffer> getRegionPrefix(DataBufferFactory bufferFactory, byte[]
118114
}
119115

120116
private Flux<DataBuffer> writeResourceRegion(ResourceRegion region, DataBufferFactory bufferFactory) {
121-
Flux<DataBuffer> in = readResourceRegion(region, bufferFactory);
122-
return DataBufferUtils.takeUntilByteCount(in, region.getCount());
123-
}
124-
125-
private Flux<DataBuffer> readResourceRegion(ResourceRegion region, DataBufferFactory bufferFactory) {
126117
Resource resource = region.getResource();
127-
try {
128-
if (resource.isFile()) {
129-
File file = region.getResource().getFile();
130-
AsynchronousFileChannel channel =
131-
AsynchronousFileChannel.open(file.toPath(), StandardOpenOption.READ);
132-
return DataBufferUtils.read(channel, region.getPosition(),
133-
bufferFactory, this.bufferSize);
134-
}
135-
}
136-
catch (IOException ignore) {
137-
// fallback to resource.readableChannel(), below
138-
}
139-
try {
140-
ReadableByteChannel channel = resource.readableChannel();
141-
Flux<DataBuffer> in = DataBufferUtils.read(channel, bufferFactory, this.bufferSize);
142-
return DataBufferUtils.skipUntilByteCount(in, region.getPosition());
143-
}
144-
catch (IOException ex) {
145-
return Flux.error(ex);
146-
}
118+
long position = region.getPosition();
119+
Flux<DataBuffer> in = DataBufferUtils.read(resource, position, bufferFactory, this.bufferSize);
120+
return DataBufferUtils.takeUntilByteCount(in, region.getCount());
147121
}
148122

149123
private Flux<DataBuffer> getRegionSuffix(DataBufferFactory bufferFactory, String boundaryString) {

spring-core/src/main/java/org/springframework/core/io/buffer/DataBufferUtils.java

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
package org.springframework.core.io.buffer;
1818

19+
import java.io.File;
1920
import java.io.IOException;
2021
import java.io.InputStream;
2122
import java.io.OutputStream;
@@ -26,6 +27,7 @@
2627
import java.nio.channels.CompletionHandler;
2728
import java.nio.channels.ReadableByteChannel;
2829
import java.nio.channels.WritableByteChannel;
30+
import java.nio.file.StandardOpenOption;
2931
import java.util.concurrent.atomic.AtomicBoolean;
3032
import java.util.concurrent.atomic.AtomicLong;
3133
import java.util.function.BiFunction;
@@ -38,6 +40,7 @@
3840
import reactor.core.publisher.FluxSink;
3941
import reactor.core.publisher.SynchronousSink;
4042

43+
import org.springframework.core.io.Resource;
4144
import org.springframework.lang.Nullable;
4245
import org.springframework.util.Assert;
4346

@@ -134,6 +137,64 @@ public static Flux<DataBuffer> read(AsynchronousFileChannel channel,
134137
});
135138
}
136139

140+
/**
141+
* Read the given {@code Resource} into a {@code Flux} of {@code DataBuffer}s.
142+
* <p>If the resource is a file, it is read into an
143+
* {@code AsynchronousFileChannel} and turned to {@code Flux} via
144+
* {@link #read(AsynchronousFileChannel, DataBufferFactory, int)} or else
145+
* fall back on {@link #read(InputStream, DataBufferFactory, int)} closes
146+
* the channel when the flux is terminated.
147+
* @param resource the resource to read from
148+
* @param dataBufferFactory the factory to create data buffers with
149+
* @param bufferSize the maximum size of the data buffers
150+
* @return a flux of data buffers read from the given channel
151+
*/
152+
public static Flux<DataBuffer> read(Resource resource,
153+
DataBufferFactory dataBufferFactory, int bufferSize) {
154+
155+
return read(resource, 0, dataBufferFactory, bufferSize);
156+
}
157+
158+
/**
159+
* Read the given {@code Resource} into a {@code Flux} of {@code DataBuffer}s
160+
* starting at the given position.
161+
* <p>If the resource is a file, it is read into an
162+
* {@code AsynchronousFileChannel} and turned to {@code Flux} via
163+
* {@link #read(AsynchronousFileChannel, DataBufferFactory, int)} or else
164+
* fall back on {@link #read(InputStream, DataBufferFactory, int)}. Closes
165+
* the channel when the flux is terminated.
166+
* @param resource the resource to read from
167+
* @param position the position to start reading from
168+
* @param dataBufferFactory the factory to create data buffers with
169+
* @param bufferSize the maximum size of the data buffers
170+
* @return a flux of data buffers read from the given channel
171+
*/
172+
public static Flux<DataBuffer> read(Resource resource, long position,
173+
DataBufferFactory dataBufferFactory, int bufferSize) {
174+
175+
try {
176+
if (resource.isFile()) {
177+
File file = resource.getFile();
178+
AsynchronousFileChannel channel =
179+
AsynchronousFileChannel.open(file.toPath(), StandardOpenOption.READ);
180+
return DataBufferUtils.read(channel, position, dataBufferFactory, bufferSize);
181+
}
182+
}
183+
catch (IOException ignore) {
184+
// fallback to resource.readableChannel(), below
185+
}
186+
187+
try {
188+
ReadableByteChannel channel = resource.readableChannel();
189+
Flux<DataBuffer> in = DataBufferUtils.read(channel, dataBufferFactory, bufferSize);
190+
return DataBufferUtils.skipUntilByteCount(in, position);
191+
}
192+
catch (IOException ex) {
193+
return Flux.error(ex);
194+
}
195+
}
196+
197+
137198
//---------------------------------------------------------------------
138199
// Writing
139200
//---------------------------------------------------------------------

spring-core/src/test/java/org/springframework/core/io/buffer/DataBufferUtilsTests.java

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
import reactor.core.publisher.Flux;
3535
import reactor.test.StepVerifier;
3636

37+
import org.springframework.core.io.ClassPathResource;
38+
import org.springframework.core.io.Resource;
39+
3740
import static org.junit.Assert.*;
3841

3942
/**
@@ -119,6 +122,33 @@ public void readInputStream() throws Exception {
119122
.verify(Duration.ofSeconds(5));
120123
}
121124

125+
@Test
126+
public void readResource() throws Exception {
127+
Resource resource = new ClassPathResource("DataBufferUtilsTests.txt", getClass());
128+
Flux<DataBuffer> flux = DataBufferUtils.read(resource, this.bufferFactory, 3);
129+
130+
StepVerifier.create(flux)
131+
.consumeNextWith(stringConsumer("foo"))
132+
.consumeNextWith(stringConsumer("bar"))
133+
.consumeNextWith(stringConsumer("baz"))
134+
.consumeNextWith(stringConsumer("qux"))
135+
.expectComplete()
136+
.verify(Duration.ofSeconds(5));
137+
}
138+
139+
@Test
140+
public void readResourcePosition() throws Exception {
141+
Resource resource = new ClassPathResource("DataBufferUtilsTests.txt", getClass());
142+
Flux<DataBuffer> flux = DataBufferUtils.read(resource, 3, this.bufferFactory, 3);
143+
144+
StepVerifier.create(flux)
145+
.consumeNextWith(stringConsumer("bar"))
146+
.consumeNextWith(stringConsumer("baz"))
147+
.consumeNextWith(stringConsumer("qux"))
148+
.expectComplete()
149+
.verify(Duration.ofSeconds(5));
150+
}
151+
122152
@Test
123153
public void writeOutputStream() throws Exception {
124154
DataBuffer foo = stringBuffer("foo");

0 commit comments

Comments
 (0)