使用Spring Boot RESTful Web流式数据 | Techshard

栏目: Java · 发布时间: 5年前

内容简介:流数据是向Web浏览器发送数据的一种全新方法,可显着加快页面加载速度。通常,我们需要允许用户在Web应用程序中下载文件。当数据太大时,提供良好的用户体验变得非常困难,使用StreamingResponseBody,我们现在可以轻松地为高度并发的应用程序流式传输数据。在本文中,我们将看一个使用

流数据是向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存储库中 找到本文的示例。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

数据结构与算法

数据结构与算法

[美] 乔兹德克 (Drozdek, A. ) / 郑岩、战晓苏 / 清华大学出版社 / 2006-1 / 69.00元

《国外计算机科学经典教材·数据结构与算法:C++版(第3版)》全面系统地介绍了计算机科学教育中的一个重要组成部分——数据结构,并以C++语言实现相关的算法。书中主要强调了数据结构和算法之间的联系,使用面向对象的方法介绍数据结构,其内容包括算法的复杂度分析、链表、栈队列、递归技术、二叉树、图、排序以及散列。《国外计算机科学经典教材·数据结构与算法:C++版(第3版)》还清晰地阐述了同类教材中较少提到......一起来看看 《数据结构与算法》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具