使用FileChannel读取文件中的块/流式传输

时间:2018-09-07 19:14:30

标签: java nio

我正在将某种文件查看器/文件浏览器实现为Web应用程序。因此,我需要从系统的硬盘中读取文件。当然,我必须处理小文件和大文件,并且我想要最快,最高效的方法。

现在,我有以下代码,如果我使用正确的方法,想问那些对有效读取(大)文件有很多了解的“大佬”:

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 :(得分:-1)

当您查看任意文件时,包括潜在的大文件,我认为这些文件实际上不是文本文件,或者您可能会遇到编码不同的文件。

因此,当您尽最大努力查看诸如文本之类的文件时,应考虑要使用哪种编码,并确保失败不会损害您的操作。与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,但是,这可能不会对性能产生太大影响。您绝对应该避免的是将所有这些块连接成一个大字符串。