Чтение чанков / потоковых файлов через FileChannel - PullRequest
0 голосов
/ 07 сентября 2018

Я реализую какой-то просмотрщик файлов / файловый менеджер в качестве веб-приложения. Поэтому мне нужно читать файлы с жесткого диска системы. Конечно, мне приходится иметь дело с небольшими файлами и большими файлами, и я хочу самый быстрый и самый эффективный способ сделать это.

Теперь у меня есть следующий код, и я хочу спросить «больших парней», которые много знают об эффективном чтении (больших) файлов, правильно ли я это делаю:

RandomAccessFile fis = new RandomAccessFile(filename, "r");
FileChannel fileChannel = fis.getChannel();
// Don't load the whole file into the memory, therefore read 4096 bytes from position on
MappedByteBuffer mappedByteBuffer = fileChannel.map(MapMode.READ_ONLY, position, 4096);
byte[] buf = new byte[4096];
StringBuilder sb = new StringBuilder();
while (mappedByteBuffer.hasRemaining()) {
  // Math.min(..) to avoid BufferUnderflowException
  mappedByteBuffer.get(buf, 0, Math.min(4096, map1.remaining()));
  sb.append(new String(buf));
}
LOGGER.debug(sb.toString()); // Debug purposes

Надеюсь, вы поможете мне и дадите несколько советов.

1 Ответ

0 голосов
/ 19 сентября 2018

Когда вы собираетесь просматривать произвольные файлы, включая потенциально большие файлы, я предполагаю, что существует вероятность того, что эти файлы на самом деле не являются текстовыми файлами или что вы можете столкнуться с файлами, которые имеют разные кодировки.

Поэтому, когда вы собираетесь просматривать такие файлы в виде текста с максимальной отдачей, вам следует подумать о том, какую кодировку вы хотите использовать, и убедиться, что сбои не повредят вашей работе. Конструктор, который вы используете с new String(buf), заменяет недопустимые символы, но излишне создавать новый экземпляр String и добавлять его к StringBuilder впоследствии.

Как правило, вы не должны идти так много обходных путей. Начиная с Java 7, вам не нужно RandomAccessFile (или FileInputStream), чтобы получить FileChannel. Простое решение будет выглядеть как

// Instead of StandardCharsets.ISO_8859_1 you could also use Charset.defaultCharset()
CharsetDecoder decoder = StandardCharsets.ISO_8859_1.newDecoder()
    .onMalformedInput(CodingErrorAction.REPLACE)
    .onUnmappableCharacter(CodingErrorAction.REPLACE)
    .replaceWith(".");

try(FileChannel fileChannel=FileChannel.open(Paths.get(filename),StandardOpenOption.READ)) {
    //Don't load the whole file into the memory, therefore read 4096 bytes from position on
    ByteBuffer mappedByteBuffer = fileChannel.map(MapMode.READ_ONLY, position, 4096);
    CharBuffer cb = decoder.decode(mappedByteBuffer);
    LOGGER.debug(cb.toString()); // Debug purposes
}

Вы можете работать с полученным CharBuffer напрямую или вызывать toString(), чтобы получить экземпляр String (но, конечно, избегайте делать это несколько раз). CharsetDecoder также позволяет повторно использовать CharBuffer, однако это может не так сильно повлиять на производительность. Чего вам определенно следует избегать, так это объединить все эти фрагменты в большую строку.

...