"Cannot dispatch without an AsyncContext" 提供文件时
"Cannot dispatch without an AsyncContext" when serving files
我需要在 "file download controller" 之上编写一个 "disposable file download" MVC 控制器。文件传输到客户端后,必须从服务器上删除它。
最初,编写代码是为了提供文件服务
进口org.springframework.core.io.Resource
@GetMapping("/get/{someParam}")
public ResponseEntity<Resource> downloadFile(Long someParam)
{
Long fileId = identify(someParam);
return super.downloadFile(fileId); //This uses a "File repository" service binding file IDs to physical paths
}
protected ResponseEntity<Resource> downloadFile(Long fileId){
File theFile = resolve(fileId);
return new FileSystemResource(theFile);
}
由于 ResponseEntity 是某种 "future" 实体,我不能删除 finally 块中的文件,因为它还没有被提供。
所以我先写了一个文件下载的异步版本,利用 Commons IO 来复制有效载荷。然后我利用回调来仅通过我的方法处理文件。
protected WebAsyncTask<Void> downloadFileAsync(Long fileId,HttpResponse response){ //API method for multiple uses across the application
InputStream is = new FileInputStream(resolve(fileId));
Callable<Void> ret = () -> {
IOUtils.copy(is,response.getOutputStream());
is.close();
return null;
};
return ret;
}
@GetMapping("/get/{someParam}")
public WebAsyncTask<Void> downloadFile(Long someParam,HttpResponse response)
{
Long fileId = identify(someParam);
WebAsyncTask ret = downloadFileAsync(fileId,response);
ret.onCompletion(()-> fileService.delete(fileId)); //Here I leverage the callback because this file, in this point, is disposable
return ret;
}
当我运行第二个版本时,出现以下错误。服务器是 Tomcat 8.0.50
10-Sep-2018 12:20:37.551 AVVERTENZA [ajp-nio-8009-exec-3] org.apache.catalina.core.AsyncContextImpl.setErrorState onError() failed for listener of type [org.apache.catalina.core.AsyncListenerWrapper]
java.lang.IllegalArgumentException: Cannot dispatch without an AsyncContext
at org.springframework.util.Assert.notNull(Assert.java:134)
at org.springframework.web.context.request.async.StandardServletAsyncWebRequest.dispatch(StandardServletAsyncWebRequest.java:128)
at org.springframework.web.context.request.async.WebAsyncManager.setConcurrentResultAndDispatch(WebAsyncManager.java:369)
at org.springframework.web.context.request.async.WebAsyncManager.access0(WebAsyncManager.java:60)
at org.springframework.web.context.request.async.WebAsyncManager.handle(WebAsyncManager.java:311)
at org.springframework.web.context.request.async.StandardServletAsyncWebRequest.onError(StandardServletAsyncWebRequest.java:144)
at org.apache.catalina.core.AsyncListenerWrapper.fireOnError(AsyncListenerWrapper.java:49)
at org.apache.catalina.core.AsyncContextImpl.setErrorState(AsyncContextImpl.java:421)
at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:370)
at org.apache.coyote.ajp.AbstractAjpProcessor.asyncDispatch(AbstractAjpProcessor.java:745)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:666)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1539)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1495)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
我已经在 web.xml
中配置了所有 servlet 和过滤器以支持异步操作。我做了一些研究,this answer 没有帮助,因为我使用的是较新的 Tomcat 版本。
我的代码有什么问题?为了简单起见,我没有完全发布它,但在调试时我看到写入操作成功并具有正确的有效负载。
我遇到了同样的问题。就我而言,解决方案是配置 AsyncTaskExecutor
:
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setDefaultTimeout(-1);
configurer.setTaskExecutor(asyncTaskExecutor());
}
@Bean
public AsyncTaskExecutor asyncTaskExecutor() {
// an implementaiton of AsyncTaskExecutor
return new SimpleAsyncTaskExecutor("async");
}
}
根据@MDenium 的评论
Don't use WebAsyncTask that is intended for internal use. Just use a CompletableFuture or return a Callable. If you put the try/finally inside your Callable it will work
WebAsyncTask 不是 API,因此当您从 MVC 方法 return 时,Spring 不知道如何处理它。这不是执行异步执行的正确方法。它仅在内部用于承载任务和上下文。
Spring MVC 支持:
- 延迟结果
- 可调用
- 可完成的未来
可能还有 少数 其他
我需要在 "file download controller" 之上编写一个 "disposable file download" MVC 控制器。文件传输到客户端后,必须从服务器上删除它。
最初,编写代码是为了提供文件服务
进口org.springframework.core.io.Resource
@GetMapping("/get/{someParam}")
public ResponseEntity<Resource> downloadFile(Long someParam)
{
Long fileId = identify(someParam);
return super.downloadFile(fileId); //This uses a "File repository" service binding file IDs to physical paths
}
protected ResponseEntity<Resource> downloadFile(Long fileId){
File theFile = resolve(fileId);
return new FileSystemResource(theFile);
}
由于 ResponseEntity 是某种 "future" 实体,我不能删除 finally 块中的文件,因为它还没有被提供。
所以我先写了一个文件下载的异步版本,利用 Commons IO 来复制有效载荷。然后我利用回调来仅通过我的方法处理文件。
protected WebAsyncTask<Void> downloadFileAsync(Long fileId,HttpResponse response){ //API method for multiple uses across the application
InputStream is = new FileInputStream(resolve(fileId));
Callable<Void> ret = () -> {
IOUtils.copy(is,response.getOutputStream());
is.close();
return null;
};
return ret;
}
@GetMapping("/get/{someParam}")
public WebAsyncTask<Void> downloadFile(Long someParam,HttpResponse response)
{
Long fileId = identify(someParam);
WebAsyncTask ret = downloadFileAsync(fileId,response);
ret.onCompletion(()-> fileService.delete(fileId)); //Here I leverage the callback because this file, in this point, is disposable
return ret;
}
当我运行第二个版本时,出现以下错误。服务器是 Tomcat 8.0.50
10-Sep-2018 12:20:37.551 AVVERTENZA [ajp-nio-8009-exec-3] org.apache.catalina.core.AsyncContextImpl.setErrorState onError() failed for listener of type [org.apache.catalina.core.AsyncListenerWrapper]
java.lang.IllegalArgumentException: Cannot dispatch without an AsyncContext
at org.springframework.util.Assert.notNull(Assert.java:134)
at org.springframework.web.context.request.async.StandardServletAsyncWebRequest.dispatch(StandardServletAsyncWebRequest.java:128)
at org.springframework.web.context.request.async.WebAsyncManager.setConcurrentResultAndDispatch(WebAsyncManager.java:369)
at org.springframework.web.context.request.async.WebAsyncManager.access0(WebAsyncManager.java:60)
at org.springframework.web.context.request.async.WebAsyncManager.handle(WebAsyncManager.java:311)
at org.springframework.web.context.request.async.StandardServletAsyncWebRequest.onError(StandardServletAsyncWebRequest.java:144)
at org.apache.catalina.core.AsyncListenerWrapper.fireOnError(AsyncListenerWrapper.java:49)
at org.apache.catalina.core.AsyncContextImpl.setErrorState(AsyncContextImpl.java:421)
at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:370)
at org.apache.coyote.ajp.AbstractAjpProcessor.asyncDispatch(AbstractAjpProcessor.java:745)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:666)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1539)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1495)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
我已经在 web.xml
中配置了所有 servlet 和过滤器以支持异步操作。我做了一些研究,this answer 没有帮助,因为我使用的是较新的 Tomcat 版本。
我的代码有什么问题?为了简单起见,我没有完全发布它,但在调试时我看到写入操作成功并具有正确的有效负载。
我遇到了同样的问题。就我而言,解决方案是配置 AsyncTaskExecutor
:
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setDefaultTimeout(-1);
configurer.setTaskExecutor(asyncTaskExecutor());
}
@Bean
public AsyncTaskExecutor asyncTaskExecutor() {
// an implementaiton of AsyncTaskExecutor
return new SimpleAsyncTaskExecutor("async");
}
}
根据@MDenium 的评论
Don't use WebAsyncTask that is intended for internal use. Just use a CompletableFuture or return a Callable. If you put the try/finally inside your Callable it will work
WebAsyncTask 不是 API,因此当您从 MVC 方法 return 时,Spring 不知道如何处理它。这不是执行异步执行的正确方法。它仅在内部用于承载任务和上下文。
Spring MVC 支持:
- 延迟结果
- 可调用
- 可完成的未来
可能还有 少数 其他