5
  Path file = Paths.get("c:/large.log");
  AsynchronousFileChannel channel = AsynchronousFileChannel.open(file);
  final ByteBuffer buffer = ByteBuffer.allocate(1000);
  channel.read(buffer, 0, buffer,
      new CompletionHandler<Integer, ByteBuffer>() {
        public void completed(Integer result, ByteBuffer attachment) {
          System.out.println(new String(buffer.array()));
        }
  }); 

In this way, I can read the first 1000 byte from large.log. How can I read following log If I don't want to allocate bigger byte array like ByteBuffer.allocate(1000*1000). Because I think this will lead to OutOfMemory.

Could someone give me the sample code? Thanks.

ps:I can loop read the large file with JIO because I can check the return value of java.io.BufferedReader.read(). But I don't know how to do with NIO2.

4

4 回答 4

6

这是一个有效的技巧。

您需要注意以下几点:

  1. 我刚刚用你buffer.array()的输出。我必须使用 buffer.clear() 来重置位置,以便异步读取会看到有 1000 个备用字节,但这不会从数组中清除现有数据。因此,当您在文件末尾时,如果您读取的字节数少于 1000 字节,它将打印整个缓冲区:无论您刚刚读取多少字节,加上缓冲区末尾最后一个字节的剩余 1000 字节。在现实生活中,您可能想要对此做一些事情(可能使用结果或缓冲区的位置。
  2. 由于某些原因,我无法弄清楚方法中buffer哪个是类变量是可以的completed,但channel哪个类变量也是空的。我还没弄清楚为什么会这样。所以我改变了它,让它channel作为附件而不是缓冲区传递。对我来说仍然没有意义。
  3. 异步读取线程对于保持 jvm 运行来说不够重要。所以我只是read在 main 方法的末尾加上了 a 。按Enter退出。
  4. 类变量pos维护您正在读取的文件中的位置。
  5. 当您在complete方法期间启动另一个异步读取时,就会发生神奇的事情。这就是我放弃匿名类并实现接口本身的原因。
  6. 您需要将路径切换回您的路径。

玩得开心。

import java.nio.*;
import java.nio.channels.*;
import java.nio.file.*;
import java.io.IOException;

public class TryNio implements CompletionHandler<Integer, AsynchronousFileChannel> {

       // need to keep track of the next position.
        int pos = 0;
        AsynchronousFileChannel channel =  null;
        ByteBuffer buffer = null;

        public void completed(Integer result, AsynchronousFileChannel attachment) {
                 // if result is -1 means nothing was read.
                if (result != -1) {
                        pos += result;  // don't read the same text again.
                                        // your output command.
                        System.out.println(new String(buffer.array()));

                        buffer.clear();  // reset the buffer so you can read more.
                }
                        // initiate another asynchronous read, with this.
                attachment.read(buffer, pos , attachment, this );


        }
        public void failed(Throwable exc,
                        AsynchronousFileChannel attachment) {
                System.err.println ("Error!");
                exc.printStackTrace();
        }

        public void doit() {
                Path file = Paths.get("/var/log/syslog");
                AsynchronousFileChannel channel =  null;
                try {
                        channel = AsynchronousFileChannel.open(file);
                } catch (IOException e) {
                        System.err.println ("Could not open file: " + file.toString());
                        System.exit(1); // yeah.  heh.
                }
                buffer = ByteBuffer.allocate(1000);

                 // start off the asynch read. 
                channel.read(buffer, pos , channel, this );
                // this method now exits, thread returns to main and waits for user input.
        }

        public static void main (String [] args) {
                TryNio tn = new TryNio();
                tn.doit();
             // wait fur user to press a key otherwise java exits because the 
             // asynch thread isn't important enough to keep it running.
                try { System.in.read(); } catch (IOException e) { }
        }
}
于 2013-10-23T05:15:44.707 回答
1

GregHNZ 解决方案很棒,因为我必须在不同的项目中多次使用这种代码,我最终将它放在一个辅助库RxIo中,我在 Maven 中央存储库中发布了该库,也可以在RxIo github 存储库中获得。使用RxIo,您可以使用RxIo实用程序类来读取文件的所有字节,例如:

AsyncFiles
    .readAllBytes(Paths.get("input.txt"))
    .thenApply(bytes -> { /*... use bytes... */});

readAllBytes(Path file)分配默认大小为 262144的ByteBuffera,但您可以使用readAllBytes(Path file, int bufferSize).

您可以在单元测试文件夹中查看其他用例。

于 2018-06-14T10:10:56.833 回答
0

如果文件中还有任何内容,则在 completionHandler 中开始另一次读取。但我会使用比 1000 大得多的缓冲区,至少 8192。

于 2013-10-23T03:03:25.790 回答
0

使用文件中的位置和文件大小,需要在完成处理程序中重复调用异步读取操作来读取整个文件。每次读取操作完成时,需要使用红色字节数增加位置。

下面是完成处理程序异步读取整个文件的完成方法。有关完整示例,请参见http://www.zoftino.com/java-asynchronous-io-nio2

public void completed(Integer result, ByteBuffer attachment) {
    try {
        bb.flip();
        System.out.println("bytea red "+bb.limit());

        if(afc.size() > position) {
            position = position + bb.limit();
            bb.clear();
            //pass the same completion handler
            afc.read(bb, position, bb, this);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }       
}
于 2018-05-30T08:37:23.137 回答