我有一个旧的应用程序,客户端通过非常慢的连接(GPRS)上传大文件。目前我们使用Spring MVC和旧的servlet 2.0标准并直接获取请求inputStream,这显然会导致长时间运行的被阻塞线程。我的任务是将应用程序升级到servlet 3.1以利用新的异步读取监听器(这被认为是直接的!)但是我已经走到了尽头。
将应用程序直接升级到servlet 3.1很顺利,忽略了我立即返回响应的事实,因为我更关心的是测试文件本身是否正确上传并且onDataAvailable()正在进行的事实多次正确调用。当我使用DeferredResult对象(看起来它或多或少是为此目的而发明)将视图名称返回给控制器时,问题就出现了,因为我需要显然让客户知道上传是否成功,在这个异步环境中我无法看到其他方法。
当我返回DeferredResult对象时,它被错误地直接返回给客户端,并且在第一次onDataAvailable()调用时,无法读取inputStream,因为我得到了一个java.io.IOException:Stream关闭异常。在这里调用getDispatcherType()告诉我类型现在已经切换到DispatcherType.ERROR,而当我运行不带DeferredResult对象的代码(并且无论如何直接响应)时,类型仍然是DispatcherType.REQUEST。我曾经想过,在堆栈的某个地方,inputStream很早就被读取了(不是由我)并且导致了这一点,但它似乎并没有,而奇怪的是inputStream.isReady()返回true而inputStream.isFinished()返回false并且我可以在第一个onDataAvailable()的缓冲区中看到文件中的文本,但是我无法直接从inputStream读取而没有获得java.io.IOException:Stream关闭异常。
使用微小文件或非常大的文件进行的任何测试都不会改变这种行为。有没有人成功使用Spring MVC中的DeferredResult和servlet 3.1?我已经被困了几天了,调试了更多的代码而不是我再做的事情了!
Heres来自我的代码(如果你觉得它看起来有点贫血的话,会剪掉一些商业资料)
@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");
}
}
答案 0 :(得分:0)
(这是我原来问题的答案,但不是整个应用程序问题的答案)。经过几个小时的调试和头部刮擦,我找到了为什么我的deferredResult未正确设置的原因。我们为一些REST utils包含了第三方库,这使得应用程序使用Spring中不推荐使用的AnnotationMethodHandlerAdapter
而不是它应该使用的RequestMappingHandlerAdapter
。一旦我覆盖了这个外部bean,就会正确处理deferredResult。
唯一的问题是这个调用流程的异步性质仍然不起作用,这可以追溯到我最初的一个子问题,即是否有人将Spring MVC / DeferredResults和Servlet 3.1一起工作。我已经看到一些stackoverflow问题问同样的事情,但遗憾的是没有答案。