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. Go语言 - 流程控制 if else | for | switch case

    流程控制 流程控制是每种编程语言控制逻辑走向和执行次序的重要部分,流程控制可以说是一门语言的“经脉”. Go语言中最常用的流程控制: if | for | switch | goto// switch ...

  2. Oracle查询一个命名空间下所有表和视图的表名、字段名、字段类型、字段大小,是否可为NULL,主键和注释信息

    使用SQL查询Oracle一个命名空间下所有表和视图的表名.字段名.字段类型.字段大小,是否可为NULL,主键和注释信息. SQL如下,注意需要将'CDFLOOD'更换为您要查询的命名空间: sele ...

  3. jquery页面多个倒计时效果

    <div class="timeBox" data-times="2019/06/30,23:59:59"> 距结束 <span class= ...

  4. YAML_14 tags给指定的任务定义一个调用标识,以后不用重复整个过程,只需要执行tags标签的部分

    ansible]# vim adhttp.yml --- - hosts: cache   remote_user: root   tasks:     - copy:         src: /r ...

  5. [nginx]nginx的一个奇葩问题 500 Internal Server Error phpstudy2018 nginx虚拟主机配置 fastadmin常见问题处理

    [nginx]nginx的一个奇葩问题 500 Internal Server Error 解决方案 nginx 一直报500 Internal Server Error 错误,配置是通过phpstu ...

  6. sudo 命令

    su命令和su -命令最大的本质区别就是: 前者只是切换了root身份,但Shell环境仍然是普通用户的Shell: 而后者连用户和Shell环境一起切换成root身份了.只有切换了Shell环境才不 ...

  7. Codeforces Round #576 (div.1 + div.2)

    Div2 A 长度为\(n(n≤10^5)\)的数组,每个元素不同,求有多少个位置\(d\)满足\(d - x \le j < d \And d < j \le d + y a_d< ...

  8. SpringMVC返回结果值处理器之返回一个值

    处理器一共分为两类: 一类是返回到一个页面,凡是返回页面的处理器,一定会刷新页面,对应的请求应该是同步请求. 一类是返回结果,凡是返回结果的处理器,一定是请求方需要得到这个结果值,此时就需要一个有回调 ...

  9. iframe中用到的例子

    <view:qrytr>         <view:qrytd colspan="4">     <iframe id="ccbl&quo ...

  10. java并发编程学习博客

    http://www.blogjava.net/xylz/archive/2010/07/08/325587.html 经典