在Java中,如何为文件的特定部分创建InputStream?

问题描述 投票:0回答:2

我需要从文件的特定部分读取的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方法是什么样的?

java file inputstream nio
2个回答
0
投票

取决于原始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的反射来查找文件名。


0
投票

有一个叫做MappedByteBuffer的东西,其内容是文件的内存映射区域。

MappedByteBufferAnother 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);
    }
}

关于锁定的系统资源的警告(在Windows上)

[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充满希望。

© www.soinside.com 2019 - 2024. All rights reserved.