我需要从文件的特定部分读取的InputStream
,仅此而已。
从InputStream的使用者的角度来看,似乎内容只是该特定部分。 Consumer<InputStream>
不会意识到其数据来自更大的文件。因此,InputStream的行为应如下:
is.read()
的调用也会返回-1
。Path file= Paths.get("file.dat");
int start = 12000;
int size = 600;
try(InputStream input = getPartialInputStream(file, start, size)){
// This should receive an inputstream that returns exactly 600 bytes.
// Those bytes should correspond to the bytes in "file.dat" found from position 12000 upto 12600.
thirdPartyMethod(input);
}
是否有很好的方法来执行此操作而不必自己实现自定义InputStream
?这样的getPartialInputStream
方法是什么样的?
取决于原始strem的来源,您可能希望丢弃它,然后返回自己的流。如果原始流支持reset()
,则接收端的用户可能会将其开头的数据设为自己可见。
public InputStream getPartialInputStream(InputStream is, int start, int size) throws IOException {
// Put your fast-forward logic here, might want to use is.skip() instead
for (int i = 0; i < start; i++) {
is.read();
}
// Rewrite the part of stream you want the caller to receive so that
// they receive *only* this part
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (int i = 0; i < size; i++) {
int read = is.read();
if (read != -1) {
baos.write(read);
} else {
break;
}
}
is.close();
return new ByteArrayInputStream(baos.toByteArray());
}
编辑为评论的答案:
如果不希望重写流,例如由于内存限制,您可以像在第一个循环中一样读取start
字节,然后使用类似Guava的ByteStreams.limit(is, size)
的方式返回流。或子类化流,并使用计数器覆盖read()
,以在读取大小后立即返回-1
。
您还可以编写一个临时文件并返回它的流-这将阻止最终用户使用原始文件的FileInputStream的反射来查找文件名。
有一个叫做MappedByteBuffer
的东西,其内容是文件的内存映射区域。
MappedByteBuffer
的Another question表示如何将此类answer映射到MappedByteBuffer
。这使我想到了这个解决方案:
InputStream
public InputStream getPartialInputStream(file, start, size) {
try (FileChannel channel = FileChannel.open(inFile, READ)) {
MappedByteBuffer content = channel.map(READ_ONLY, start, size);
return new ByteBufferBackedInputStream(content);
}
}
[public class ByteBufferBackedInputStream extends InputStream {
ByteBuffer buf;
public ByteBufferBackedInputStream(ByteBuffer buf) {
this.buf = buf;
}
public int read() throws IOException {
if (!buf.hasRemaining()) {
return -1;
}
return buf.get() & 0xFF;
}
public int read(byte[] bytes, int off, int len)
throws IOException {
if (!buf.hasRemaining()) {
return -1;
}
len = Math.min(len, buf.remaining());
buf.get(bytes, off, len);
return len;
}
}
遭受MappedByteBuffer
的困扰,其中基础文件被映射的缓冲区锁定,直到缓冲区本身被垃圾回收为止,并且周围没有干净的方法。
因此您可以仅在以后不必删除/移动/重命名文件的情况下才使用此解决方案。尝试导致出现bug(除非您很幸运,缓冲区已被垃圾回收)。
我不确定我是否应该很快对这个java.nio.file.AccessDeniedException
充满希望。