AsynchronousFileChannel如何读取大文件?

时间:2013-10-23 03:00:59

标签: io java-7 nio2

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

通过这种方式,我可以从large.log中读取前1000个字节。如何读取以下日志如果我不想像ByteBuffer.allocate(1000 * 1000)那样分配更大的字节数组。因为我认为这将导致OutOfMemory。

有人可以给我示例代码吗? 感谢。

ps:我可以使用JIO循环读取大文件,因为我可以检查java.io.BufferedReader.read()的返回值。但我不知道如何处理NIO2。

4 个答案:

答案 0 :(得分:4)

这是一个有效的黑客。

您需要注意的几件事情:

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

    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) { }
            }
    }
    

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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

下面是completehandler完成异步读取整个文件的方法。有关完整示例,请参阅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();
    }       
}

答案 3 :(得分:0)

GregHNZ解决方案非常棒,因为我必须在不同的项目中多次使用这种代码,所以我最终将它放在我在Maven Central Repository中发布的辅助库AsyncFileRw中,也可以在AsyncFileRw github存储库。使用AsyncFileRw,您可以使用AsyncFiles实用程序类来读取文件的所有字节,如:

Path in = Paths.get("input.txt");
CompletableFuture<byte[]> bytesCf = AsyncFiles.readAllBytes(in);
bytesCf.thenApply(bytes -> { /*... read bytes... */});

readAllBytes(Path file)分配ByteBuffer,默认大小为262144,但您可以使用readAllBytes(Path file, int bufferSize)指定其他值。

您可以在unit tests folder中查看其他用例。