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. 织梦个人空间中调用ip,会员类型,邮箱,金币,会员积分

    织梦个人空间中调用.用户昵称,最后登录,会员等级 ,会员头衔,会员积分,空间访问,邮箱地址 ,金币数量,会员组的有效期天数 ,升级会员组的时间 ,用户的等级,用户的性别 ,会员的类型,ip 第一步确定 ...

  2. httpclient工具使用(org.apache.httpcomponents.httpclient)

    httpclient工具使用(org.apache.httpcomponents.httpclient) 引入依赖 <dependency> <groupId>org.apac ...

  3. postgreSQL \timing 查看SQL执行时间

    postgreSQL中,关于查询执行时间的\timing的使用

  4. CODE FESTIVAL 2016 Grand Final 题解

    传送门 越学觉得自己越蠢--这场除了\(A\)之外一道都不会-- \(A\) 贪心从左往右扫,能匹配就匹配就好了 //quming #include<bits/stdc++.h> #def ...

  5. Vue.js 十五分钟入门

    本文经授权转载,仅用于学习,版权归原作者所有. TypeScript 为 JavaScript 带来静态类型检查,让 JavaScript 编写中大型应用的时候可以应用工具来避免部分错误. Vue 很 ...

  6. 《自制编程语言--基于C语言 郑钢》学习笔记

    <自制编程语言>学习笔记 本仓库内容 <自制编程语言>源码 src/sparrow.tgz <自制编程语言>读书笔记 docs/* <自制编程语言>样章 ...

  7. C Primer Plus--C存储类、链接和内存管理之存储类(storage class)

    目录 存储类 作用域 链接 存储时期 自动变量 寄存器变量 具有代码块作用域的静态变量 具有外部链接的静态变量 extern关键字 具有内部链接的静态变量 多文件 存储类 C为变量提供了5种不同的存储 ...

  8. sys 模块常用方法

    sys.argv 命令行参数List,第一个元素是程序本身路径 sys.modules.keys() 返回所有已经导入的模块列表 sys.exc_info() 获取当前正在处理的异常类,exc_typ ...

  9. SpringMVC的处理器全局异常处理类

    SpringMVC的处理器全局异常处理类 package com.huawei.utils; import org.springframework.web.servlet.HandlerExcepti ...

  10. LVM卷

    sdb和sdc创建为LVM并且挂载到/benet/ 将sdd扩展到之前的lvm卷 新建2块1G的磁盘和1块2G的磁盘 将分区ID改为8e 创建PV阶段 pvcreate /dev/sdb1 /dev/ ...