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