With the release of Spring 4.2 version, Three new classes have been introduced to handle Requests Asynchronously of the Servlet Thread. Which are;
  1. ResponseBodyEmitter
  2. SseEmitter
  3. StreamingResponseBody
ResponseBodyEmitter enables to send DeferredResult with a compatible HttpMessageConverter. Results can be emitted from threads which are not necessarily the Servlet Request Thread of the Servlet Container.
 
SseEmitter is used to send Server Sent Events to the Client. Server Sent Events has a fixed format and the response type for the result will be text/event-stream.
 
StreamingResponseBody is used to send raw unformatted data such as bytes to the client asynchronously of the Servlet Thread.
 
ResponseBodyEmitter and SseEmitter has a method named complete to mark its completion and StreamingResponseBody will complete when there is no more data to send. 
 
All three options will be keeping alive a connection to the endpoint until the end of the request.
 
StreamingResponseBody is particularly useful for streaming large files such as Media Files as writing of the bytes to the Response's OutputStream will be done asynchronously. StreamingResponseBody has a writeTo(OutputStream os) call back method which needs to be overridden inorder to support streaming.
 
I wrote a small Spring Boot Application to showcase the StreamingResponseBody capabilities in terms of Streaming large files. The application source code can be found at www.github.com/shazin/itube. Below is a screen shot of the application.
In order to send the Video files streaming to the Projekktor player in the web page following code snippet is used.
@RequestMapping(method = RequestMethod.GET, value = "/{video:.+}")
public StreamingResponseBody stream(@PathVariable String video)
throws FileNotFoundException {
File videoFile = videos.get(video);
final InputStream videoFileStream = new FileInputStream(videoFile);
return (os) -> {
readAndWrite(videoFileStream, os);
};
}

And a Custom Web Configuration to over ride default timeout behavior to no timeout and finally configuring an AsyncTaskExecutor

@Configuration
public static class WebConfig extends WebMvcConfigurerAdapter { @Override
public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
configurer.setDefaultTimeout(-1);
configurer.setTaskExecutor(asyncTaskExecutor());
} @Bean
public AsyncTaskExecutor asyncTaskExecutor() {
return new SimpleAsyncTaskExecutor("async");
} }

转载自:http://shazsterblog.blogspot.com/2016/02/asynchronous-streaming-request.html

笔者代码:

    @RequestMapping(value = "{uuid}.m3u8")
public ResponseEntity<StreamingResponseBody> m3u8Generator(@PathVariable("uuid") String uuid){ String key = "media.".concat(uuid);
Map<String, Object> cached = cacheService.getCacheMap(key);
if(CollectionUtils.isEmpty(cached))
{
return new ResponseEntity(null, HttpStatus.OK);
}
String playlist = (String) cached.get("playlist");
String[] lines = playlist.split("\n"); //人为在每个MPEG-2 transport stream文件前面加上一个地址前缀
StringBuffer buffer = new StringBuffer(); StreamingResponseBody responseBody = new StreamingResponseBody() {
@Override
public void writeTo (OutputStream out) throws IOException {
for(int i = 0; i < lines.length; i++)
{
String line = lines[i]; if(line.endsWith(".ts"))
{
buffer.append("/streaming/");
buffer.append(uuid);
buffer.append("/");
buffer.append(line);
}else {
buffer.append(line);
}
buffer.append("\r\n");
}
out.write(buffer.toString().getBytes());
out.flush();
}
}; return new ResponseEntity(responseBody, HttpStatus.OK);
}

其他类似功能代码:

//https://www.baeldung.com/spring-mvc-sse-streams
//https://www.logicbig.com/tutorials/spring-framework/spring-web-mvc/streaming-response-body.html
//@GetMapping("/{fileName:[0-9A-z]+}")
@GetMapping("/streaming2/{uuid}/{fileName}")
//https://stackoverflow.com/questions/47868352/how-to-stream-large-http-response-in-spring
@ResponseBody
public ResponseEntity<InputStreamResource> get_File2(
@PathVariable String uuid,
@PathVariable String fileName) throws IOException { String dbFile = fileRepository.findByUUID(uuid,fileName); dbFile=String.format("E:/hls/test/%s",fileName); if (dbFile.equals(null)) {
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
} Resource file = storageService.loadAsResource(dbFile); long len = 0;
try {
len = file.contentLength();
} catch (IOException e) {
e.printStackTrace();
} MediaType mediaType = MediaType.valueOf(FileTypeMap.getDefaultFileTypeMap().getContentType(file.getFile())); // if (filename.toLowerCase().endsWith("mp4") || filename.toLowerCase().endsWith("mp3") ||
// filename.toLowerCase().endsWith("3gp") || filename.toLowerCase().endsWith("mpeg") ||
// filename.toLowerCase().endsWith("mpeg4"))
if(fileName.toLowerCase().endsWith("ts")) {
mediaType = MediaType.APPLICATION_OCTET_STREAM;
} InputStreamResource resource = new InputStreamResource(new FileInputStream(file.getFile())); return ResponseEntity.ok()
.contentType(mediaType)
.contentLength(len)
.header(HttpHeaders.ACCEPT_RANGES, "bytes")
.body(resource);
}
    @RequestMapping("/111111")
public StreamingResponseBody handleRequest (HttpServletRequest request) {
return outputStream -> {
Map<String, BigInteger> map = new HashMap<>();
map.put("one", BigInteger.ONE);
map.put("ten", BigInteger.TEN);
try(ObjectOutputStream oos = new ObjectOutputStream(outputStream)){
oos.writeObject(map);
}
};
}
    @GetMapping("/media/{uuid}/{fileName}")
@ResponseBody
public ResponseEntity<ByteArrayResource> getStream(@PathVariable String uuid,
@PathVariable String fileName) throws IOException {
String key = "media.".concat(uuid);
String encoded = cacheService.getCachedHashValue(key, fileName, String.class);
byte[] bytes = Base64.getDecoder().decode(encoded); long len = bytes.length; //InputStreamResource resource = new InputStreamResource(new FileInputStream(file.getFile()));
ByteArrayResource resource = new ByteArrayResource(bytes); return ResponseEntity.ok().contentType(MediaType.APPLICATION_OCTET_STREAM).contentLength(len).header(HttpHeaders.ACCEPT_RANGES, "bytes").body(resource);
// return ResponseEntity.ok()
// .contentType(MediaType.APPLICATION_OCTET_STREAM)
// .contentLength(len)
// .header(HttpHeaders.ACCEPT_RANGES, "bytes")
// .body(resource);
}

Asynchronous Streaming Request Processing in Spring MVC 4.2 + Spring Boot(SpringBoot中处理异步流请求 SpringMvc4.2以上)的更多相关文章

  1. 2017.3.31 spring mvc教程(六)转发、重定向、ajax请求

    学习的博客:http://elf8848.iteye.com/blog/875830/ 我项目中所用的版本:4.2.0.博客的时间比较早,11年的,学习的是Spring3 MVC.不知道版本上有没有变 ...

  2. Spring MVC普通类或工具类中调用service报空空指针的解决办法(调用service报java.lang.NullPointerException)

    当我们在非Controller类中应用service的方法是会报空指针,如图: 这是因为Spring MVC普通类或工具类中调用service报空null的解决办法(调用service报java.la ...

  3. Spring MVC 零配置 / Spring MVC JavaConfig

    1. Spring MVC的核心就是DispatcherServlet类,Spring MVC处理请求的流程如下图所示: 2. Spring MVC中典型的上下文层次 当我们初始化一个Dispatch ...

  4. spring mvc注解和spring boot注解

    1 spring mvc和spring boot之间的关系 spring boot包含spring mvc.所以,spring mvc的注解在spring boot总都是可以用的吗? spring b ...

  5. java之spring mvc之初始spring mvc

    1. mvc : mvc框架是处理 http请求和响应的框架 2. mvc 做的事情有哪些: 将 url 映射到一个java的处理方法上 将表单数据提交到 java 类中 将后台 java 类处理的结 ...

  6. Spring MVC mapping[From Spring MVC Beginner's Guide]

    In a Spring MVC application, the URL can logically be divided into five parts (see the following fig ...

  7. Spring MVC 学习笔记 spring mvc Schema-based configuration

    Spring mvc 目前支持5个tag,分别是 mvc:annotation-driven,mvc:interceptors,mvc:view-controller, mvc:resources和m ...

  8. Spring MVC(十六)--Spring MVC国际化实例

    上一篇文章总结了一下Spring MVC中实现国际化所需的配置,本文继上一文举一个完整的例子,我选择用XML的方式.我的场景是这样的: 访问一个页面时,这个页面有个表格,对表头中的列名实现国际化. 第 ...

  9. 玩转spring mvc(四)---在spring MVC中整合JPA

    关于在Spring MVC中整合JPA是在我的上一篇关于spring mvc基本配置基础上进行的,所以大家先参考一下我的上一篇文章:http://blog.csdn.net/u012116457/ar ...

随机推荐

  1. 依赖注入(DI)和控制反转(IOC)

    学习过Spring框架的人一定都会听过Spring的IoC(控制反转) .DI(依赖注入)这两个概念,对于初学Spring的人来说,总觉得IoC .DI这两个概念是模糊不清的,是很难理解的,今天和大家 ...

  2. python字符转数字

    print(float("260.01478420521632365364543423")) 260.0147842052163

  3. lambda 函数的用法

    lambda函数又叫匿名函数, 匿名函数就是没有名字的函数,不使用def语句声明的函数.如果要声名,则需要使用lambda关键字进行声明. 一般用来定义简单的函数. 1.声明一个简单的加法匿名函数: ...

  4. 一.使用LDAP认证

    作用:网络用户认证,用户集中管理 网络用户信息:LDAP服务器提供 本地用户信息:/etc/passwd /etc/shadow提供     LDAP服务器:虚拟机classroom     LDAP ...

  5. 计数器的原理,设计及verilog实现

    若计数器由n个触发器组成,则计数器的位数为n,所能计数的最大模数为2的n次幂.以下为同步二进制加法计数器电路; 驱动方程:状态图 状态方程(此时的Q0,Q1为上一次状态值): 下例是同步4位2进制计数 ...

  6. 【数论】P1029 最大公约数和最小公倍数问题

    题目链接 P1029 最大公约数和最小公倍数问题 思路 如果有两个数a和b,他们的gcd(a,b)和lcm(a,b)的乘积就等于ab. 也就是: ab=gcd(a,b)*lcm(a,b) 那么,接下来 ...

  7. 洛谷 P3143 [USACO16OPEN]钻石收藏家Diamond Collector 题解

    P3143 [USACO16OPEN]钻石收藏家Diamond Collector 题目描述 Bessie the cow, always a fan of shiny objects, has ta ...

  8. 关于nginx反代jenkins报错 反向代理设置有误

    官方文档地址: https://wiki.jenkins.io/display/JENKINS/Running+Jenkins+behind+Nginx 直接解决的配置文件吧. 这是使用子域名,不使用 ...

  9. C#中的线程之Abort陷阱

    .简介 C#中通常使用线程类Thread来进行线程的创建与调度,博主在本文中将分享多年C#开发中遇到的Thread使用陷阱. Thread调度其实官方文档已经说明很详细了.本文只简单说明,不做深入探讨 ...

  10. UMD、CommonJS、ES Module、AMD、CMD模块的写法

    AMD异步模块规范 RequireJS就是AMD的一个典型的实现. 以下是一个只依赖与jQuery的模块代码: // foo.js define(['jquery'], function($){ // ...