我有一个老的应用程序,客户端通过非常缓慢的连接(GPRS)上传大文件。目前我们使用Spring MVC和旧的servlet 2.0标准并直接获取请求inputStream,这显然会导致长时间的阻塞线程。我被委托将应用程序升级到servlet 3.1,以利用新的异步读取监听器(这被认为是一件简单的事情!),但我遇到了死胡同。
将应用程序直接升级到servlet 3.1进行测试时,一切都很顺利,尽管我更关注测试文件本身是否被正确上传并且onDataAvailable()是否已被正确调用。问题出现在我去使用DeferredResult对象(它似乎是专门为此而发明的)将视图名称返回给控制器时,因为我需要让客户端知道上传成功或失败,我看不到在这个异步环境中完成此操作的其他方法。
当我返回DeferredResult对象时,它被错误地立即返回给客户端,在第一个onDataAvailable()调用中无法从inputStream中读取,因为我得到了一个java.io.IOException: Stream closed异常。对getDispatcherType()的调用告诉我类型现在已经转换为DispatcherType.ERROR,而在没有DeferredResult对象(并且立即响应)的情况下运行代码时,类型仍为DispatcherType.REQUEST。我曾认为在堆栈中的某个位置,inputStream是被早期读取(不是我),并导致了这个问题,但似乎并不是这样,奇怪的是,inputStream.isReady()返回true,inputStream.isFinished()返回false,并且我可以在第一个onDataAvailable()中看到文件缓冲区中的文本,但我无法直接从inputStream中读取,而不会得到java.io.IOException: Stream closed异常。
无论使用小文件还是大文件进行多少次测试都无法改变此行为。有没有人成功地在Spring MVC和servlet 3.1中使用DeferredResult? 我已经陷入困境几天了,调试了比我想要的代码更多的东西! 这里是我的代码(一些业务内容已剪辑以防您认为它看起来有点贫瘠)。
将应用程序直接升级到servlet 3.1进行测试时,一切都很顺利,尽管我更关注测试文件本身是否被正确上传并且onDataAvailable()是否已被正确调用。问题出现在我去使用DeferredResult对象(它似乎是专门为此而发明的)将视图名称返回给控制器时,因为我需要让客户端知道上传成功或失败,我看不到在这个异步环境中完成此操作的其他方法。
当我返回DeferredResult对象时,它被错误地立即返回给客户端,在第一个onDataAvailable()调用中无法从inputStream中读取,因为我得到了一个java.io.IOException: Stream closed异常。对getDispatcherType()的调用告诉我类型现在已经转换为DispatcherType.ERROR,而在没有DeferredResult对象(并且立即响应)的情况下运行代码时,类型仍为DispatcherType.REQUEST。我曾认为在堆栈中的某个位置,inputStream是被早期读取(不是我),并导致了这个问题,但似乎并不是这样,奇怪的是,inputStream.isReady()返回true,inputStream.isFinished()返回false,并且我可以在第一个onDataAvailable()中看到文件缓冲区中的文本,但我无法直接从inputStream中读取,而不会得到java.io.IOException: Stream closed异常。
无论使用小文件还是大文件进行多少次测试都无法改变此行为。有没有人成功地在Spring MVC和servlet 3.1中使用DeferredResult? 我已经陷入困境几天了,调试了比我想要的代码更多的东西! 这里是我的代码(一些业务内容已剪辑以防您认为它看起来有点贫瘠)。
@Controller
public class ContentController {
@RequestMapping(value = “/upload/“, method = POST)
public DeferredResult<String> upload(ServletRequest request) throws IOException, InterruptedException {
DeferredResult<String> deferredResult = new DeferredResult<String>();
final AsyncContext asyncContext = httpServletRequest.startAsync();
ServletInputStream servletInputStream = httpServletRequest.getInputStream();
NioReadListener readListener = new NioReadListener(servletInputStream, asyncContext, deferredResult, size);
servletInputStream.setReadListener(readListener);
return deferredResult
}
public class NioReadListener implements ReadListener {
private final ServletInputStream _input;
private final AsyncContext _context;
private final DeferredResult<String> deferredResult;
public NioReadListener(ServletInputStream servletInputStream, AsyncContext asyncContext, DeferredResult<String> deferredResult, Long size) throws IOException {
this._input = servletInputStream;
this._context = asyncContext;
this.deferredResult = deferredResult;
}
@Override
public void onDataAvailable() throws IOException {
try {
int bytesRead;
byte b[] = new byte[_size.intValue()];
while (_input.isReady() && (bytesRead = _input.read(b)) != -1) {
_totalBytesRead += bytesRead;
}
} catch (IOException e) {
_log.error(e);
}
}
@Override
public void onAllDataRead() throws IOException {
this._context.complete();
deferredResult.setResult("VIEW_NAME");
}
}
<async-supported>true</async-supported>
。 - jnyDispatcherServlet
有一个检查finally { if (asyncManager.isConcurrentHandlingStarted()) { return; } }
,由于我的asyncWebRequest
部分在asyncManager
中是null
,即使asyncManager
的其余属性已设置,它也不会返回true。现在只需要弄清楚那一部分... - Jonathan Keenan