0

在阅读旧的InputStream时,我使用了以下代码(我从来不习惯):

    int read = 0;
    InputStream is = ....;

    while((i = is.read() != -1){

        ....
    }

现在我正在尝试使用 NIO从InputStream读取 10MB :

        protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        // TODO Auto-generated method stub

        System.out.println("In Controller.doPost(...)");

        ByteBuffer chunk = ByteBuffer.allocateDirect(1000000);

        /* Source channel */
        int numRead = 0;
        ReadableByteChannel rbc = Channels.newChannel(request.getInputStream());

        /* Destination channel */
        File destFile = new File(
                "D:\\SegyDest.sgy");
        FileOutputStream destFileFos = new FileOutputStream(destFile);
        FileChannel destFileChannel = destFileFos.getChannel();

        /* Read-Write code */
        while (numRead >= 0) {
            chunk.rewind();
            numRead = rbc.read(chunk);

            System.out.println("numRead = " + numRead);

            chunk.rewind();
            destFileChannel.write(chunk);
        }

        /* clean-up */
        rbc.close();
        destFileChannel.close();
        destFileFos.close();

        request.setAttribute("ops", "File Upload");
        request.getRequestDispatcher("/jsp/Result.jsp").forward(request,
                response);
    }

我的问题是/* 如何遍历源通道来读取所有字节?*/

4

2 回答 2

1

或者以超过 1 个字节的块执行 IO,API 如下所示:

byte[] bA = new byte[4096];
int i;
InputStream is = ....;
OutputStream os = ....;

while((i = is.read(bA) != -1){
    os.write(bA, 0, i);
}

我看过你的另一个问题,我的评论仍然有效。NIO 不是您正在寻找的解决方案。您有一台低端机器,其 RAM 限制作为代理。

你能做的最好的事情就是让你的 Servlet 创建一个新线程,让这个线程使用 NIO 套接字/HTTP 库创建并设置一个传出连接。这个新的(和额外的)线程正在等待 3 件事中的任何一件发生,它推动任何 API 尝试在这 3 个领域取得进展。

这3件事是:

  • 尝试向远程服务器写入数据(如果内存中有缓冲数据要发送)
  • 等待主 Servlet 线程指示共享缓冲区中有新数据。或者达到了流的结束。
  • 等待主 Servlet 线程指示额外的线程需要关闭(这是错误恢复和清理)。

您可能需要 doPost() 方法在额外线程上调用的 drainWithTimeout(long millis) 函数,以便给它一定的时间将最终数据推送到远程服务器。如果 Servlet 从 InputStream 观察到 End-of-Stream,则会调用此方法。

在 doPost() 方法返回之前,您必须确保您的额外线程 100% 可靠地获得。所以控制它的启动/关闭很重要,尤其是在 InputStream 因为发送客户端断开或空闲时间太长而出错的情况下。

然后两个线程(doPost() 中的普通 Servlet 线程和您创建的新线程)将设置并共享一些任意内存缓冲区,可能共享 16Mb 或更多。

如果由于客户端/并发用户和 2Gb RAM 的限制而无法拥有 16Mb 缓冲区,那么您真的应该坚持使用此答案顶部的示例代码,因为网络和 O/S 内核已经缓冲了一些 Mb数据的。

使用两个线程的要点是您无法解决接收数据的 Servlet API 是阻塞 I/O API 的问题,如果您正在编写符合 Servlet 规范/标准的应用程序,则无法更改该问题。如果您知道您的特定 Servlet 容器具有某个功能,那么这超出了此答案的范围。

这两个线程允许主 Servlet doPost 线程处于控制之中,并且仍然为 InputStream 使用阻塞 I/O API。

将一个线程和一个阻塞 InputStream 与一个非阻塞 OutputStream 一起使用是没有意义的,您仍然会遇到在 in.read() API 调用被阻塞时无法为输出流提供服务的问题(等待更多数据或 End-流)。

于 2012-09-20T09:51:30.407 回答
0

NIO通道之间拷贝的正确方法如下:

while (in.read(buffer) > 0 || buffer.position() > 0)
{
  buffer.flip();
  out.write(buffer);
  buffer.compact();
}

请注意,这会自动处理 EOS、部分读取和部分写入。

于 2012-09-24T10:59:58.617 回答