问题描述
我遇到的问题与和此处.
我尝试了给出的答案及其组合,但是没有一个解决我的问题.
I tried the answers given and combinations thereof but none solved my issue.
当我尝试此答案时,在30秒后(而不是超时),下载从头开始,然后,又过了30秒,然后超时了.
When I tried this answer, after 30 seconds, instead of the timeout, the download restarted from the beginning and then, after 30 more seconds, then it timed out.
我正在通过访问Google Chrome浏览器中的REST端点并尝试从中下载文件进行测试.
I'm testing by visiting the REST endpoint in Google Chrome and trying to download a file from there.
此处我有显示此错误的项目.
Here I have the project that displays this error.
谢谢.
:来源:
package io.github.guiritter.transferer_local;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class TransfererLocalApplication {
public static void main(String[] args) {
SpringApplication.run(TransfererLocalApplication.class, args);
}
}
package io.github.guiritter.transferer_local;
import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.rest.webmvc.RepositoryRestController;
// import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
@RepositoryRestController
@RequestMapping("api")
public class DefaultController {
@Value("${fileName}")
private String fileName;
@Value("${filePath}")
private String filePath;
@GetMapping("download")
public StreamingResponseBody downloadHub(HttpServletResponse response) throws IOException {
File file = new File(filePath + fileName);
response.setContentType(APPLICATION_OCTET_STREAM_VALUE);
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
response.setHeader("Content-Length", file.length() + "");
InputStream inputStream = new FileInputStream(file);
return outputStream -> {
int nRead;
byte[] data = new byte[1024*1024];
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
outputStream.write(data, 0, nRead);
}
inputStream.close();
};
}
// @GetMapping("download")
// public ResponseEntity<StreamingResponseBody> downloadHub(HttpServletResponse response) throws IOException {
// File file = new File(filePath + fileName);
// response.setContentType(APPLICATION_OCTET_STREAM_VALUE);
// response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
// response.setHeader("Content-Length", file.length() + "");
// InputStream inputStream = new FileInputStream(file);
// return ResponseEntity.ok(outputStream -> {
// int nRead;
// byte[] data = new byte[1024*1024];
// while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
// outputStream.write(data, 0, nRead);
// }
// inputStream.close();
// });
// }
}
package io.github.guiritter.transferer_local;
// import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@EnableAsync
@EnableScheduling
public class AsyncConfiguration implements AsyncConfigurer {
@Override
@Bean(name = "taskExecutor")
public AsyncTaskExecutor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(Integer.MAX_VALUE);
executor.setThreadNamePrefix("io.github.guiritter.transferer_local.async_executor_thread.");
return executor;
}
/** Configure async support for Spring MVC. */
@Bean
public WebMvcConfigurer webMvcConfigurerAdapter(
AsyncTaskExecutor taskExecutor) {
return new WebMvcConfigurer() {
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer
.setDefaultTimeout(Long.MAX_VALUE)
.setTaskExecutor(taskExecutor);
configureAsyncSupport(configurer);
}
};
}
// @Autowired
// private AsyncTaskExecutor taskExecutor;
// /** Configure async support for Spring MVC. */
// @Bean
// public WebMvcConfigurer webMvcConfigurerAdapter() {
// return new WebMvcConfigurer() {
// @Override
// public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
// configurer
// .setDefaultTimeout(Long.MAX_VALUE)
// .setTaskExecutor(taskExecutor);
// configureAsyncSupport(configurer);
// }
// };
// }
}
package io.github.guiritter.transferer_local;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
@EnableWebMvc
@EnableTransactionManagement
@EnableAsync
public class MyConfiguration implements WebMvcConfigurer {
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setDefaultTimeout(-1);
}
}
server.port=8081
fileName=large_file_name.txt
filePath=C:\\path\\to\\large\\file\\
# spring.mvc.async.request-timeout = 9223372036854775807
# spring.mvc.async.request-timeout = 2147483647
spring.mvc.async.request-timeout = -1
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>io.github.guiritter</groupId>
<artifactId>transferer-local</artifactId>
<version>1.0.0</version>
<name>TransfererLocal</name>
<description>Enables local network file transfer</description>
<properties>
<java.version>14</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.data/spring-data-rest-webmvc -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-rest-webmvc</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
更新:尝试Manuel的答案(已提交给答案answer_Manuel分支):
Update: trying Manuel's answer (commited to branch answer_Manuel):
package io.github.guiritter.transferer_local;
import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.Callable;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.rest.webmvc.RepositoryRestController;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.async.WebAsyncTask;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
@RepositoryRestController
@RequestMapping("api")
public class DefaultController {
@Value("${fileName}")
private String fileName;
@Value("${filePath}")
private String filePath;
@GetMapping("download")
public WebAsyncTask<ResponseEntity<StreamingResponseBody>> downloadHub(HttpServletResponse response) throws IOException {
File file = new File(filePath + fileName);
response.setContentType(APPLICATION_OCTET_STREAM_VALUE);
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
response.setHeader("Content-Length", file.length() + "");
InputStream inputStream = new FileInputStream(file);
return new WebAsyncTask<ResponseEntity<StreamingResponseBody>>(Long.MAX_VALUE, () ->
ResponseEntity.<StreamingResponseBody>ok(outputStream -> {
int nRead;
byte[] data = new byte[1024*1024];
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
outputStream.write(data, 0, nRead);
}
inputStream.close();
})
);
}
}
它引发了 AsyncRequestTimeoutException
,并且:
java.lang.IllegalArgumentException: Cannot dispatch without an AsyncContext
at org.springframework.util.Assert.notNull(Assert.java:198) ~[spring-core-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.context.request.async.StandardServletAsyncWebRequest.dispatch(StandardServletAsyncWebRequest.java:131) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.context.request.async.WebAsyncManager.setConcurrentResultAndDispatch(WebAsyncManager.java:391) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.context.request.async.WebAsyncManager.lambda$startCallableProcessing$2(WebAsyncManager.java:315) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.springframework.web.context.request.async.StandardServletAsyncWebRequest.lambda$onError$0(StandardServletAsyncWebRequest.java:146) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at java.base/java.util.ArrayList.forEach(ArrayList.java:1510) ~[na:na]
at org.springframework.web.context.request.async.StandardServletAsyncWebRequest.onError(StandardServletAsyncWebRequest.java:146) ~[spring-web-5.2.2.RELEASE.jar:5.2.2.RELEASE]
at org.apache.catalina.core.AsyncListenerWrapper.fireOnError(AsyncListenerWrapper.java:49) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.core.AsyncContextImpl.setErrorState(AsyncContextImpl.java:422) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.catalina.connector.CoyoteAdapter.asyncDispatch(CoyoteAdapter.java:239) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.coyote.AbstractProcessor.dispatch(AbstractProcessor.java:237) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:59) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:860) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1591) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130) ~[na:na]
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630) ~[na:na]
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) ~[tomcat-embed-core-9.0.29.jar:9.0.29]
at java.base/java.lang.Thread.run(Thread.java:832) ~[na:na]
更新:尝试使用Manuel更新后的答案(已提交给分支机构answer_Manuel_2020-04-06):
Update: trying Manuel's updated answer (commited to branch answer_Manuel_2020-04-06):
package io.github.guiritter.transferer_local;
import static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.Callable;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.rest.webmvc.RepositoryRestController;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.async.WebAsyncTask;
import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody;
@RepositoryRestController
@RequestMapping("api")
public class DefaultController {
@Value("${fileName}")
private String fileName;
@Value("${filePath}")
private String filePath;
@GetMapping("download")
public ResponseEntity<StreamingResponseBody> downloadHub() throws IOException {
File file = new File(filePath + fileName);
InputStream inputStream = new FileInputStream(file);
return ResponseEntity
.ok()
.contentType(APPLICATION_OCTET_STREAM)
.header("Content-Disposition", "attachment; filename=" + fileName)
.header("Content-Length", file.length() + "")
.<StreamingResponseBody>body(outputStream -> {
int nRead;
byte[] data = new byte[1024*1024];
while ((nRead = inputStream.read(data, 0, data.length)) != -1) {
outputStream.write(data, 0, nRead);
}
inputStream.close();
});
}
}
推荐答案
要解决此问题,请执行以下操作:
将 @RepositoryRestController
更改为例如 @RestController
.
如果使用 @RepositoryRestController
,则会为 RequestMappingHandlerAdapter
设置超时.但是,在请求下载后, RepositoryRestHandlerAdapter
会处理该请求,因为注释要求他这样做.
If you use @RepositoryRestController
, the timeout will be set for the RequestMappingHandlerAdapter
. But upon requesting the download, the RepositoryRestHandlerAdapter
handles the request, as the annotation requires him to.
如果使用 @RestController
,则正确的 RequestMappingHandlerAdapter
将处理下载,超时设置为-1.
If you use the @RestController
then the (correct) RequestMappingHandlerAdapter
will handle the download, with the timeout set to -1.
原始答案:
您可以尝试通过返回 org.springframework.web.context.request.async.WebAsyncTask .
如果提供设置 Callable< V>
,具有 timeout
:
If offers to set a Callable<V>
with a timeout
:
然后您的DefaultController可能看起来像:
Then your DefaultController could look like:
public WebAsyncTask<ResponseEntity<StreamingResponseBody>> downloadHub() throws IOException {
...
new WebAsyncTask<ResponseEntity<StreamingResponseBody>>(myTimeOutAsLong, callable);
}
更新:
- 请从您的REST控制器方法中删除
HttpServletResponse
参数.只需确定,HttpServletResponse
中的OutputStream
不会干扰StreamingResponseBody
中的OutputStream
. - 关于错误无法在没有AsyncContext的情况下无法分派":如果没有AsyncContext,则无法调度提供文件时
- Please remove the
HttpServletResponse
parameter from your REST controller method. Just to be sure, that theOutputStream
from theHttpServletResponse
does not interfere with theOutputStream
fromStreamingResponseBody
. - Regarding the error ´Cannot dispatch without an AsyncContext´: "Cannot dispatch without an AsyncContext" when serving files
这篇关于Spring REST端点在30秒后返回StreamingResponseBody:AsyncRequestTimeoutException的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!