Как я могу создать ограниченный InputStream для чтения только части файла? - PullRequest
10 голосов
/ 22 мая 2010

Я хочу создать InputStream, который ограничен определенным диапазоном байтов в файле, например в байты от позиции 0 до 100. Таким образом, клиентский код должен видеть EOF после достижения 100-го байта.

Ответы [ 7 ]

10 голосов
/ 22 мая 2010

Метод read() для InputStream читает один байт за раз. Вы можете написать подкласс InputStream, который поддерживает внутренний счетчик; Каждый раз, когда вызывается read(), обновляйте счетчик. Если вы достигли максимума, не разрешайте дальнейшее чтение (верните -1 или что-то подобное).

Вам также необходимо убедиться, что другие методы чтения read_int и т. Д. Не поддерживаются (например: переопределите их и просто сгенерируйте UnsupportedOperationException ());

Я не знаю, каков ваш вариант использования, но в качестве бонуса вы можете также захотеть реализовать буферизацию.

8 голосов
/ 22 мая 2010

Как сказал Данбен , просто украсьте свой поток и примените ограничение:

public class ConstrainedInputStream extends InputStream {
  private final InputStream decorated;
  private long length;

  public ConstrainedInputStream(InputStream decorated, long length) {
    this.decorated = decorated;
    this.length = length;
  }

  @Override public int read() throws IOException {
    return (length-- <= 0) ? -1 : decorated.read();
  }

  // TODO: override other methods if you feel it's necessary
  // optionally, extend FilterInputStream instead
}
4 голосов
/ 23 мая 2010
2 голосов
/ 23 декабря 2015

В дополнение к этому решению , используя skip метод InputStream, вы также можете прочитать диапазон, начиная с середины файла.

public class RangeInputStream extends InputStream
{
    private InputStream parent;
    private long remaining;

    public RangeInputStream(InputStream parent, long start, long end) throws IOException
    {
        if (end < start)
        {
            throw new IllegalArgumentException("end < start");
        }

        if (parent.skip(start) < start)
        {
            throw new IOException("Unable to skip leading bytes");
        }

        this.parent=parent;
        remaining = end - start;
    }

    @Override
    public int read() throws IOException
    {
        return --remaining >= 0 ? parent.read() : -1;
    }
}
2 голосов
/ 07 февраля 2014

Вы можете использовать ByteStreams гуавы. Обратите внимание, что вы должны использовать skipFully () до лимита, например:

ByteStreams.skipFully(tmpStream, range.start());
tmpStream = ByteStreams.limit(tmpStream, range.length());
2 голосов
/ 22 мая 2010

Если вам нужны только 100 байтов, тогда, вероятно, лучше использовать простой, я бы считал их в массив и обернул их как ByteArrayInputStream.Например,

   int length = 100;
   byte[] data = new byte[length];
   InputStream in = ...;  //your inputstream
   DataInputStream din = new DataInputStream(din);
   din.readFully(data);
   ByteArrayInputStream first100Bytes = new ByteArrayInputStream(data);
   // pass first100bytes to your clients

Если вы не хотите использовать DataInputStream.readFully, в Apache commons-io есть IOUtils.readFully, или вы можете явно выполнить цикл чтения.

Если выиметь более сложные потребности, такие как чтение из сегмента в середине файла или большие объемы данных, затем расширение InputStream и переопределение read (byte [], int, int), а также read (), даст вамлучшая производительность, чем просто переопределение метода read ().

0 голосов
/ 03 ноября 2017

Мне удалось решить аналогичную проблему для моего проекта, рабочий код вы можете увидеть здесь PartInputStream .Я использовал его для потоков ввода ресурсов и файлов.Но он не подходит для потоков, длина которых изначально недоступна, таких как сетевые потоки.

...