流数据是向Web浏览器发送数据的一种全新方法,可显着加快页面加载速度。通常,我们需要允许用户在Web应用程序中下载文件。当数据太大时,提供良好的用户体验变得非常困难,使用StreamingResponseBody,我们现在可以轻松地为高度并发的应用程序流式传输数据。
在本文中,我们将看一个使用StreamingResponseBody下载文件的示例。在这种方法中,数据被处理并以块的形式写入OutputStream。
设置Spring Boot项目
为Maven POM添加一些基本依赖项:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<groupId>com.techshard.streamingresponse</groupId> <artifactId>springboot-download</artifactId> <version>1.0-SNAPSHOT</version>
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.4.RELEASE</version> <relativePath /> </parent>
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-io</artifactId> <version>1.3.2</version> </dependency> </dependencies> </project>
|
我们现在将创建一个控制器并添加一个API端点以供下载。这是完整的控制器。@RestController @RequestMapping ("/api") public class DownloadController {
private final Logger logger = LoggerFactory.getLogger(DownloadController.class);
@GetMapping (value = "/download", produces = MediaType.APPLICATION_JSON_VALUE) public ResponseEntity<StreamingResponseBody> download(final HttpServletResponse response) {
response.setContentType("application/zip"); response.setHeader( "Content-Disposition", "attachment;filename=sample.zip");
StreamingResponseBody stream = out -> {
final String home = System.getProperty("user.home"); final File directory = new File(home + File.separator + "Documents" + File.separator + "sample"); final ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream());
if(directory.exists() && directory.isDirectory()) { try { for (final File file : directory.listFiles()) { final InputStream inputStream=new FileInputStream(file); final ZipEntry zipEntry=new ZipEntry(file.getName()); zipOut.putNextEntry(zipEntry); byte[] bytes=new byte[1024]; int length; while ((length=inputStream.read(bytes)) >= 0) { zipOut.write(bytes, 0, length); } inputStream.close(); } zipOut.close(); } catch (final IOException e) { logger.error("Exception while reading and streaming data {} ", e); } } }; logger.info("steaming response {} ", stream); return new ResponseEntity(stream, HttpStatus.OK); } }
|
在此API端点中,我们从目录中读取多个文件并创建zip文件。我们在StreamingResponseBody中执行此过程。在使用 ResponseEntity将写入的信息传递回客户端之前, 会将数据直接写入OutputStream 。这意味着下载过程将立即在客户端上启动,而服务器正在处理并以块的形式写入数据。
动服务器并使用http:// localhost:8080 / api / download测试此端点 。
使用StreamingResponseBody时,强烈建议配置Spring MVC中使用的TaskExecutor来执行异步请求。TaskExecutor是一个抽象Runnable执行的接口。
让我们配置TaskExecutor。这是AsyncConfiguration类,它使用WebMvcCofigurer配置超时,并且还注册在超时时调用的拦截器,以防您需要一些特殊处理。
@Configuration @EnableAsync @EnableScheduling public class AsyncConfiguration implements AsyncConfigurer {
private final Logger log = LoggerFactory.getLogger(AsyncConfiguration.class);
@Override @Bean (name = "taskExecutor") public AsyncTaskExecutor getAsyncExecutor() { log.debug("Creating Async Task Executor"); ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(25); return executor; }
@Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return new SimpleAsyncUncaughtExceptionHandler(); }
/** Configure async support for Spring MVC. */ @Bean public WebMvcConfigurer webMvcConfigurerConfigurer(AsyncTaskExecutor taskExecutor, CallableProcessingInterceptor callableProcessingInterceptor) { return new WebMvcConfigurer() { @Override public void configureAsyncSupport(AsyncSupportConfigurer configurer) { configurer.setDefaultTimeout(360000).setTaskExecutor(taskExecutor); configurer.registerCallableInterceptors(callableProcessingInterceptor); WebMvcConfigurer.super.configureAsyncSupport(configurer); } }; }
@Bean public CallableProcessingInterceptor callableProcessingInterceptor() { return new TimeoutCallableProcessingInterceptor() { @Override public <T> Object handleTimeout(NativeWebRequest request, Callable<T> task) throws Exception { log.error("timeout!"); return super.handleTimeout(request, task); } }; } }
|
在GitHub存储库中找到本文的示例。