如何将InputStream复制到AsynchronousFileChannel

7

我想从(Tomcat servlet)的InputStream中读取数据,并使用AsynchronousFileChannel异步地将(大量)内容复制到文件中。 我可以使用常规FileChannel进行操作,如此处所述,并且了解关于缺失的transferTo的信息。 但是,如果我使用Java 7的AsyncFileChannel,则始终会出现BufferOverflowException。

    try (AsynchronousFileChannel output = AsynchronousFileChannel.open(path, StandardOpenOption.CREATE, StandardOpenOption.WRITE);
         output.lock(); // need to lock, this is one key reason to use channel

        ReadableByteChannel input = Channels.newChannel(inputStream); // servlet InputStream
        ByteBuffer buf = ByteBuffer.allocate(4096);
        int position = 0;
        int count;
        Future<Integer> lastWrite = null;
        while ((count = input.read(buf)) >= 0 || buf.position() > 0) {
            logger.info("read {} bytes", count);
            buf.flip();
            output.write(buf, position);
            if (count > 0) position += count;
            buf.compact();
        }
        if (lastWrite != null) lastWrite.get(10, TimeUnit.SECONDS);

然后当我运行时,就会出现以下情况:
14:12:30.597 [http-bio-9090-exec-3] INFO  c.b.p.c.BlobUploadServlet - read 4096 bytes
14:12:30.597 [http-bio-9090-exec-3] INFO  c.b.p.c.BlobUploadServlet - read 0 bytes
... many more with 0 bytes read ...
14:12:30.597 [http-bio-9090-exec-3] INFO  c.b.p.c.BlobUploadServlet - read 3253 bytes
14:12:30.605 [http-bio-9090-exec-3] ERROR c.b.p.c.BlobUploadServlet - null
java.nio.BufferOverflowException: null
at java.nio.HeapByteBuffer.put(HeapByteBuffer.java:183) ~[na:1.7.0_17]
at java.nio.channels.Channels$ReadableByteChannelImpl.read(Channels.java:393) ~[na:1.7.0_17]

我该如何修复缓冲区溢出?另外,当读取0字节时,暂停循环并等待的适当方法是什么?

你还在寻找答案吗?还是已经解决了? - Jose Martinez
你从哪里获取 inputStream?错误是由于调用了 HeapByteBuffer.put,并传入了一个太大的字节数组,导致无法容纳,但是 Channels.ReadableByteChannel.read 似乎是正确的,除非 inputStream.read 返回的大小超过了传递给它的最大值。 (那将是 InputStream 的错误实现,但是 HeapByteBufferReadableByteChannel 的源代码似乎是正确的。) - Cel Skeggs
1个回答

3

虽然对于原帖已经来不及了,但还是想提供一些帮助。

我尝试重现了你的问题(但使用了略微不同的示例,我利用通道复制了大文件):

public static void main(String[] args) throws IOException,  InterruptedException, ExecutionException {
    final InputStream inputStream = new FileInputStream("/home/me/Store/largefile");
    final ReadableByteChannel inputChannel = Channels.newChannel(inputStream);
    final AsynchronousFileChannel outputChannel = AsynchronousFileChannel.open(
                    FileSystems.getDefault().getPath(
                    "/home/me/Store/output"),
                    StandardOpenOption.CREATE, StandardOpenOption.WRITE);
    outputChannel.lock();

    final ByteBuffer buffer = ByteBuffer.allocate(4096);
    int position = 0;
    int recievedBytes = 0;
    Future<Integer> lastWrite = null;

    while ((recievedBytes = inputChannel.read(buffer)) >= 0
            || buffer.position() != 0) {
        System.out.println("Recieved bytes: " + recievedBytes);
        System.out.println("Buffer position: " + buffer.position());
        buffer.flip();
        lastWrite = outputChannel.write(buffer, position);
        // do extra work while asynchronous channel is writing bytes to disk,
        // in perfect case more extra work can be done, not just simple calculations
        position += recievedBytes;
        // extra work is done, we should wait, because we use only one buffer which can be still busy
        if (lastWrite != null)  lastWrite.get();
        buffer.compact();
    }

    outputChannel.close();
    inputChannel.close();
    inputStream.close();
}

在每个循环迭代中,我们从输入流中读取一块数据,然后将此块数据“推送”到输出流。当前线程不等待写入完成,而是继续执行,因此我们可以进行额外的工作。但在新的迭代之前,我们应该等待写入完成。尝试注释掉 if (lastWrite != null) lastWrite.get(); 您将会得到 java.nio.BufferOverflowException
您的代码给了我使用Future处理最后一次写操作的提示。但你忽略了等待最后一次操作的步骤。
另外,我省略了您片段中的一些额外调整(仅为简单起见,在处理文件时不需要额外调整)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接