在阅读古老的 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);
}
我的问题是 / *如何循环源通道读取所有字节? * /
答案 0 :(得分:1)
或者在API的1个字节以上的块中执行IO,如下所示:
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库创建和设置传出连接。这个新的(和额外的)线程正在等待发生的任何事情中的任何一个,它会推动任何API尝试并在这三个方面取得进展。
这三件事是:
你可能需要一个drainWithTimeout(long millis)函数,doPost()方法调用额外的线程给它一些时间来将最终数据推送到远程服务器。如果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线程处于控制状态,STILL使用阻塞I / O API进行InputStream。
没有必要使用一个线程和一个阻塞的InputStream和一个非阻塞的OutputStream,你仍然会遇到这样的问题:当in.read()API调用被阻塞时你无法为输出流提供服务(等待更多的数据)或流结束)。
答案 1 :(得分:0)
在NIO频道之间复制的正确方法如下:
while (in.read(buffer) > 0 || buffer.position() > 0)
{
buffer.flip();
out.write(buffer);
buffer.compact();
}
请注意,这会自动处理EOS,部分读取和部分写入。