您可以尝试使用StreamingResponseBody。
StreamingResponseBody
这是一种控制器方法返回值类型,用于异步请求处理,应用程序可以直接写入响应OutputStream而不会阻塞Servlet容器线程。
由于您正在单独的线程上工作,并直接向响应写入数据,因此在return
之前调用close()
方法的问题已得到解决。
您可以从以下示例开始尝试:
public ResponseEntity<StreamingResponseBody> export(...) throws FileNotFoundException {
InputStream inputStream = new FileInputStream(new File("/path/to/example/file"));
StreamingResponseBody responseBody = outputStream -> {
int numberOfBytesToWrite;
byte[] data = new byte[1024];
while ((numberOfBytesToWrite = inputStream.read(data, 0, data.length)) != -1) {
System.out.println("Writing some bytes..");
outputStream.write(data, 0, numberOfBytesToWrite);
}
inputStream.close();
};
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=generic_file_name.bin")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(responseBody);
}
您也可以尝试使用Files
(自Java 7以来)
这样您就不必管理InputStream
File file = new File("/path/to/example/file");
StreamingResponseBody responseBody = outputStream -> {
Files.copy(file.toPath(), outputStream);
};
正如@Stackee007在评论中所描述的,在生产环境下在重负载下,定义一个@Configuration
类用于调整参数和管理Async
进程是一个好的实践。
@Configuration
@EnableAsync
@EnableScheduling
public class AsyncConfiguration implements AsyncConfigurer {
private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class);
private final TaskExecutionProperties taskExecutionProperties;
public AsyncConfiguration(TaskExecutionProperties taskExecutionProperties) {
this.taskExecutionProperties = taskExecutionProperties;
}
@Override
@Bean(name = "taskExecutor")
public Executor getAsyncExecutor() {
log.debug("Creating Async Task Executor");
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(taskExecutionProperties.getPool().getCoreSize());
executor.setMaxPoolSize(taskExecutionProperties.getPool().getMaxSize());
executor.setQueueCapacity(taskExecutionProperties.getPool().getQueueCapacity());
executor.setThreadNamePrefix(taskExecutionProperties.getThreadNamePrefix());
return executor;
}
@Bean
protected WebMvcConfigurer webMvcConfigurer() {
return new WebMvcConfigurer() {
@Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setTaskExecutor(getTaskExecutor());
}
};
}
@Bean
protected ConcurrentTaskExecutor getTaskExecutor() {
return new ConcurrentTaskExecutor(this.getAsyncExecutor());
}
@Override
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return new SimpleAsyncUncaughtExceptionHandler();
}
}
如何使用mockMvc进行测试
您可以在集成测试中按照以下示例代码进行操作:
.andExpect(request().asyncStarted())
.andDo(MvcResult::getAsyncResult)
.andExpect(status().isOk()).getResponse().getContentAsByteArray();
ResponseEntity<StreamingResponseBody>
的内容类型在这个例子中是 MediaType.APPLICATION_OCTET_STREAM
,你可以通过调用 .getContentAsByteArray()
方法获取字节数组,但是根据响应体的内容类型不同,你也可以获取到 String/Json/纯文本等其它格式的内容。
GET
时发生,而不会在使用HEAD
时发生。 - Marged