Refactor HTTP Range support with ResourceRegion
Prior to this commit, the `ResourceHttpMessageConverter` would support all HTTP Range requests and `MethodProcessors` would "wrap" controller handler return values with a `HttpRangeResource` to support that use case in Controllers. This commit refactors that support in several ways: * a new ResourceRegion class has been introduced * a new, separate, ResourceRegionHttpMessageConverter handles the HTTP range use cases when serving static resources with the ResourceHttpRequestHandler * the support of HTTP range requests on Controller handlers has been removed until a better solution is found Issue: SPR-14221, SPR-13834
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.core.io;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Region of a {@link Resource} implementation, materialized by a {@code position}
|
||||
* within the {@link Resource} and a byte {@code count} for the length of that region.
|
||||
* @author Arjen Poutsma
|
||||
* @since 4.3.0
|
||||
*/
|
||||
public class ResourceRegion {
|
||||
|
||||
private final Resource resource;
|
||||
|
||||
private final long position;
|
||||
|
||||
private final long count;
|
||||
|
||||
/**
|
||||
* Create a new {@code ResourceRegion} from a given {@link Resource}.
|
||||
* This region of a resource is reprensented by a start {@code position}
|
||||
* and a byte {@code count} within the given {@code Resource}.
|
||||
* @param resource a Resource
|
||||
* @param position the start position of the region in that resource
|
||||
* @param count the byte count of the region in that resource
|
||||
*/
|
||||
public ResourceRegion(Resource resource, long position, long count) {
|
||||
Assert.notNull(resource, "'resource' must not be null");
|
||||
Assert.isTrue(position >= 0, "'position' must be larger than or equal to 0");
|
||||
Assert.isTrue(count >= 0, "'count' must be larger than or equal to 0");
|
||||
this.resource = resource;
|
||||
this.position = position;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the underlying {@link Resource} for this {@code ResourceRegion}
|
||||
*/
|
||||
public Resource getResource() {
|
||||
return this.resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the start position of this region in the underlying {@link Resource}
|
||||
*/
|
||||
public long getPosition() {
|
||||
return this.position;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the byte count of this region in the underlying {@link Resource}
|
||||
*/
|
||||
public long getCount() {
|
||||
return this.count;
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@ import java.nio.charset.Charset;
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Phillip Webb
|
||||
* @author Brian Clozel
|
||||
* @since 3.2.2
|
||||
* @see FileCopyUtils
|
||||
*/
|
||||
@@ -131,6 +132,43 @@ public abstract class StreamUtils {
|
||||
return byteCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a range of content of the given InputStream to the given OutputStream.
|
||||
* <p>If the specified range exceeds the length of the InputStream, this copies
|
||||
* up to the end of the stream and returns the actual number of copied bytes.
|
||||
* <p>Leaves both streams open when done.
|
||||
* @param in the InputStream to copy from
|
||||
* @param out the OutputStream to copy to
|
||||
* @param start the position to start copying from
|
||||
* @param end the position to end copying
|
||||
* @return the number of bytes copied
|
||||
* @throws IOException in case of I/O errors
|
||||
* @since 4.3.0
|
||||
*/
|
||||
public static long copyRange(InputStream in, OutputStream out, long start, long end) throws IOException {
|
||||
long skipped = in.skip(start);
|
||||
if (skipped < start) {
|
||||
throw new IOException("Skipped only " + skipped + " bytes out of " + start + " required.");
|
||||
}
|
||||
long bytesToCopy = end - start + 1;
|
||||
byte buffer[] = new byte[StreamUtils.BUFFER_SIZE];
|
||||
while (bytesToCopy > 0) {
|
||||
int bytesRead = in.read(buffer);
|
||||
if (bytesRead == -1) {
|
||||
break;
|
||||
}
|
||||
else if (bytesRead <= bytesToCopy) {
|
||||
out.write(buffer, 0, bytesRead);
|
||||
bytesToCopy -= bytesRead;
|
||||
}
|
||||
else {
|
||||
out.write(buffer, 0, (int) bytesToCopy);
|
||||
bytesToCopy = 0;
|
||||
}
|
||||
}
|
||||
return end - start + 1 - bytesToCopy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drain the remaining content of the given InputStream.
|
||||
* Leaves the InputStream open when done.
|
||||
|
||||
Reference in New Issue
Block a user