5 java asynchronous servlets spring-mvc resttemplate
呼吁所有 Spring 框架专家,
设想
用户A向 Spring 控制器执行
GET请求,后者GET向远程主机发出另一个请求以获取文件,该文件的内容将流式传输(缓冲区字节复制)给初始用户A作为响应。
PS:Spring Controller 的作用就像一个代理
使用
问题
好像没有可用的 Servlet 请求线程或者其他东西被阻止了......
在下面列出的所有情况下,第一个用户都可以启动文件下载。不幸的是,Spring会停止调用控制器下载方法,直到第一次下载完成为止(但有时,它会在用户等待 XX 秒后调用它)。尝试过的方法
@Async在包含方法的服务上- 当响应输出缓冲区为( )时RestTemplate抛出。服务-方法。NullPointerExceptionbyte copy operationnullat org.apache.coyote.http11.Http11OutputBuffer$SocketOutputBuffer.doWrite(Http11OutputBuffer.java:561)downloadStreamingResponseBody@Async即使包含在返回的服务级别中,也无法解决并发下载问题AsyncResult<StreamingResponseBody>。服务-downloadAsync方法。也许有人知道在 Spring 中执行此操作的更好方法?
方法
RestTemplate其执行FileCopyUtils.copy(downloadResponse.getBody(), userResponse.getOutputStream());范围为ResponseExtractor.HttpsURLConnectionFileCopyUtils.copy(downloadResponse.getBody(), userResponse.getOutputStream());在控制器返回时执行StreamingResponseBody 所有列出的方法都有效,它们将文件内容从一台服务器响应传输InputStream到用户响应OutputStream。但是,并发下载存在问题(Spring停止调用控制器的下载方法)。
来源
私人信息(网址、身份验证等)已被替换,代码严格出于问题演示目的而编写
调度员
通过扩展构造AbstractAnnotationConfigDispatcherServletInitializer.
控制器
包含不同方法的 3 个端点。
@RequestMapping(value = "/download/way1", method = RequestMethod.GET)
public void requestDownloadPage(final HttpServletResponse downloadResponse, final HttpServletRequest downloadRequest) throws ExecutionException, InterruptedException, URISyntaxException {
dwn.download(downloadResponse);
}
@RequestMapping(value = "/download/way2", method = RequestMethod.GET)
public StreamingResponseBody requestDownloadPage2(final HttpServletResponse response) throws ExecutionException, InterruptedException, URISyntaxException {
return dwn.downloadAsync(response).get();
}
@RequestMapping(value = "/download/way3", method = RequestMethod.GET)
public StreamingResponseBody requestDownloadPage3(final HttpServletResponse response) throws ExecutionException, InterruptedException, URISyntaxException, IOException {
return outputStream -> {
URL url = new URL("https://some/url/path/veryBig.zip");
HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
connection.setDoOutput(false);
connection.setDoInput(true);
connection.setUseCaches(false);
connection.setRequestProperty ("Authorization", "Basic " + Base64.getEncoder().encodeToString("username:password".getBytes(StandardCharsets.UTF_8)));
connection.setRequestMethod("GET");
response.addHeader(HttpHeaders.CONTENT_TYPE, connection.getHeaderField(HttpHeaders.CONTENT_TYPE));
response.addHeader(HttpHeaders.CONTENT_LENGTH, connection.getHeaderField(HttpHeaders.CONTENT_LENGTH));
String disposition = connection.getHeaderField(HttpHeaders.CONTENT_DISPOSITION);
if (disposition != null && !disposition.isEmpty()) {
response.addHeader(HttpHeaders.CONTENT_DISPOSITION, disposition);
}
try (InputStream inputStream = connection.getInputStream();) {
FileCopyUtils.copy(inputStream, outputStream);
} catch (Throwable any) {
// failed
}
};
}
Run Code Online (Sandbox Code Playgroud)
服务
public void download(HttpServletResponse downloadResponse) {
final RestTemplate restTemplate = new RestTemplate();
RequestCallback requestCallback = new RequestCallback() {
@Override
public void doWithRequest(ClientHttpRequest request) throws IOException {
request.getHeaders().set(HttpHeaders.AUTHORIZATION, "Basic " + Base64.getEncoder().encodeToString("username:password".getBytes(StandardCharsets.UTF_8)));
}
};
ResponseExtractor<Void> responseExtractor = response -> {
List<String> type = response.getHeaders().get(HttpHeaders.CONTENT_TYPE);
List<String> length = response.getHeaders().get(HttpHeaders.CONTENT_LENGTH);
List<String> disposition = response.getHeaders().get(HttpHeaders.CONTENT_DISPOSITION);
downloadResponse.addHeader(HttpHeaders.CONTENT_TYPE, type.get(0));
downloadResponse.addHeader(HttpHeaders.CONTENT_LENGTH, length.get(0));
if (disposition != null && !disposition.isEmpty()) {
downloadResponse.addHeader(HttpHeaders.CONTENT_DISPOSITION, disposition.get(0));
}
FileCopyUtils.copy(response.getBody(), downloadResponse.getOutputStream());
return null;
};
restTemplate.execute("https://some/url/path/veryBig.zip",
HttpMethod.GET,
requestCallback,
responseExtractor);
}
@Async
public Future<StreamingResponseBody> downloadAsync(HttpServletResponse response) {
final RestTemplate restTemplate = new RestTemplate();
RequestCallback requestCallback = new RequestCallback() {
@Override
public void doWithRequest(ClientHttpRequest request) throws IOException {
request.getHeaders().set(HttpHeaders.AUTHORIZATION, "Basic " + Base64.getEncoder().encodeToString("username:password".getBytes(StandardCharsets.UTF_8)));
}
};
ResponseExtractor<StreamingResponseBody> responseExtractor = responseOwnCloud -> {
List<String> type = responseOwnCloud.getHeaders().get(HttpHeaders.CONTENT_TYPE);
List<String> length = responseOwnCloud.getHeaders().get(HttpHeaders.CONTENT_LENGTH);
List<String> disposition = responseOwnCloud.getHeaders().get(HttpHeaders.CONTENT_DISPOSITION);
response.setHeader(HttpHeaders.CONTENT_TYPE, type.get(0));
response.setHeader(HttpHeaders.CONTENT_LENGTH, length.get(0));
if (disposition != null && !disposition.isEmpty()) {
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, disposition.get(0));
}
return outputStream -> {
FileCopyUtils.copy(responseOwnCloud.getBody(), response.getOutputStream());
};
};
return new AsyncResult<>(restTemplate.execute("https://some/url/path/veryBig.zip",
HttpMethod.GET,
requestCallback,
responseExtractor));
}
Run Code Online (Sandbox Code Playgroud)
Spring异步配置
@Configuration
@ComponentScan(basePackages = { "some.service"})
@EnableAsync
public class AsyncConfig implements AsyncConfigurer {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(4);
executor.setMaxPoolSize(4);
executor.setQueueCapacity(500);
executor.setThreadNamePrefix("AsyncExec-");
executor.initialize();
return executor;
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3286 次 |
| 最近记录: |