如何使用 Spring MVC 返回视频,以便可以使用 html5 <video> 标签进行导航?

Posted

技术标签:

【中文标题】如何使用 Spring MVC 返回视频,以便可以使用 html5 <video> 标签进行导航?【英文标题】:How do I return a video with Spring MVC so that it can be navigated using the html5 <video> tag? 【发布时间】:2014-01-05 05:48:03 【问题描述】:

如果我在 Web 服务器 (Tomcat) 中有一个文件并创建了一个标签,我可以观看视频、暂停、浏览它,并在完成后重新启动它。

但如果我创建一个 REST 接口,在请求时发送视频文件,并将其 URL 添加到标签,我只能播放和暂停。 无后退、无快进、无导航,什么都没有。

那么,有没有办法解决这个问题?我在某处遗漏了什么吗?

视频文件与 REST 接口在同一台服务器上,REST 接口只检查会话,并在找出应该发送哪个视频后发送视频。

这些是我迄今为止尝试过的方法。它们都可以工作,但没有一个允许导航。

方法一,响应实体:

/*
 * This will actually load the whole video file in a byte array in memory,
 * so it's not recommended.
 */
@RequestMapping(value = "/id/preview", method = RequestMethod.GET)
@ResponseBody public ResponseEntity<byte[]> getPreview1(@PathVariable("id") String id, HttpServletResponse response) 
    ResponseEntity<byte[]> result = null;
    try 
        String path = repositoryService.findVideoLocationById(id);
        Path path = Paths.get(pathString);
        byte[] image = Files.readAllBytes(path);

        response.setStatus(HttpStatus.OK.value());
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
        headers.setContentLength(image.length);
        result = new ResponseEntity<byte[]>(image, headers, HttpStatus.OK);
     catch (java.nio.file.NoSuchFileException e) 
        response.setStatus(HttpStatus.NOT_FOUND.value());
     catch (Exception e) 
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
    
    return result;

方法二,流复制:

/*
 * IOUtils is available in Apache commons io
 */
@RequestMapping(value = "/id/preview2", method = RequestMethod.GET)
@ResponseBody public void getPreview2(@PathVariable("id") String id, HttpServletResponse response) 
    try 
        String path = repositoryService.findVideoLocationById(id);
        File file = new File(path)
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        response.setHeader("Content-Disposition", "attachment; filename="+file.getName().replace(" ", "_"));
        InputStream iStream = new FileInputStream(file);
        IOUtils.copy(iStream, response.getOutputStream());
        response.flushBuffer();
     catch (java.nio.file.NoSuchFileException e) 
        response.setStatus(HttpStatus.NOT_FOUND.value());
     catch (Exception e) 
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
    

方法三,文件系统资源:

@RequestMapping(value = "/id/preview3", method = RequestMethod.GET)
@ResponseBody public FileSystemResource getPreview3(@PathVariable("id") String id, HttpServletResponse response) 
    String path = repositoryService.findVideoLocationById(id);
    return new FileSystemResource(path);

【问题讨论】:

我相信如果视频正在流式传输,则无法导航,而不是完全可用的文件。但我不确定。 我喜欢第三种方法。方法 1 很昂贵,并且方法 1 和 2 都违反了 MVC 模式(通过使用 HttpServletResponseOutputStream)。控制器应将视图任务委托给ViewHttpMessageConverter 不幸的是,两者都不足以用于视频流,因为必须处理部分 http 请求。我添加了一个控制器类,它管理这些类型的请求,完全独立于任何服务。 【参考方案1】:

要支持 Safari,您必须处理范围请求并提供正确的 206 返回码。 https://www.rfc-editor.org/rfc/rfc7233

https://melgenek.github.io/spring-video-service 是一个 Spring 示例。

【讨论】:

同意。处理部分请求是执行此操作的唯一方法,它适用于所有浏览器。【参考方案2】:

处理非静态资源的简单解决方案:

@SpringBootApplication
public class DemoApplication 

    private final static File MP4_FILE = new File("/home/ego/bbb_sunflower_1080p_60fps_normal.mp4");

    public static void main(String[] args) 
        SpringApplication.run(DemoApplication.class, args);
    

    @Controller
    final static class MyController 

        @Autowired
        private MyResourceHttpRequestHandler handler;

        // supports byte-range requests
        @GetMapping("/")
        public void home(
                HttpServletRequest request,
                HttpServletResponse response
        ) throws ServletException, IOException 

            request.setAttribute(MyResourceHttpRequestHandler.ATTR_FILE, MP4_FILE);
            handler.handleRequest(request, response);
        

        // does not support byte-range requests
        @GetMapping(path = "/plain", produces = "video/mp4")
        public FileSystemResource plain() 

            return new FileSystemResource(MP4_FILE);
        
    

    @Component
    final static class MyResourceHttpRequestHandler extends ResourceHttpRequestHandler 

        private final static String ATTR_FILE = MyResourceHttpRequestHandler.class.getName() + ".file";

        @Override
        protected Resource getResource(HttpServletRequest request) throws IOException 

            final File file = (File) request.getAttribute(ATTR_FILE);
            return new FileSystemResource(file);
        
    

(受 Spring Boots LogFileMvcEndpoint 启发,或多或少等于我后来发现的 Paul-Warrens (@paul-warren) StoreByteRangeHttpRequestHandler)。

希望这是 Spring 将在不久的将来支持的东西,请参阅 https://jira.spring.io/browse/SPR-13834(请投票)。

【讨论】:

ERROR 12362 --- [nio-8080-exec-2] oaccC[.[.[/].[dispatcherServlet] :Servlet.service() 用于 servlet [dispatcherServlet] 在上下文中的路径 [ ] 抛出异常 [Circular view path [plain]: 将再次分派回当前处理程序 URL [/plain]。检查您的 ViewResolver 设置! (提示:由于默认视图名称生成,这可能是未指定视图的结果。)] 根本原因 在 oops 建议中添加 @ResponseBody 即可解决问题 既然 Spring 支持该功能,您能否更新您的答案? @BrianClozel 你能告诉我这在哪里支持吗?任何链接,在 Spring 中,我尝试查看 Spring 参考文档和上述 jira,但找不到任何链接。【参考方案3】:

我知道这是一篇旧帖子,但如果它对其他提出相同/类似问题的人有用。

现在有像Spring Content 这样的项目本身就支持视频流。最简单的实现所需的所有代码是:-

@StoreRestResource(path="videos")
public interface VideoStore extends Store<String> 

这足以创建一个 Java API 和一组允许您 PUT/POST、GET 和 DELETE 视频流的 REST 端点。 GET 支持字节范围,可以在 html5 视频播放器等中正常播放。

【讨论】:

听起来不错。我会仔细看看的。谢谢【参考方案4】:

HTTP 恢复下载功能可能是您的朋友。我以前也有同样的问题。实现 http 范围后,视频中的导航是可能的:

http://balusc.blogspot.com/2009/02/fileservlet-supporting-resume-and.html

【讨论】:

这正是问题所在!在修改该页面中的脚本以满足我的需要(Spring-MVC 等)之后,它运行良好。我可以从日志中看到 Chrome 确实在进行多次调用,发出部分请求并尊重缓存配置。非常感谢,你拯救了我的一天:-)【参考方案5】:

其实是前端显示

每个浏览器针对一种特殊的视频格式,都有一个默认的控制面板。

您可以使用 html 和 css 通过媒体 API 创建自己的控件。 Media api

来自Div into HTML5 [默认情况下,

【讨论】:

我知道,但我认为这个控件的行为会根据文件的接收方式而有所不同。 我不这么认为。它只是 video 标签 src 属性的 url。浏览器只知道从 url 加载字节以及视频是否已完全下载。它不知道服务器是否将所有字节都放入响应中。 事实证明 Chrome 做得更多。它会尝试部分下载视频,尽管在指向文件时默认启用该功能,但我必须手动对其进行编码,就像 @user3395533 所说的

以上是关于如何使用 Spring MVC 返回视频,以便可以使用 html5 <video> 标签进行导航?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Spring MVC 中返回 403 Forbidden?

如何正确实现使用 Spring MVC\Boot 返回图像列表的 REST 服务?

如何使用 Spring Security / Spring MVC 处理表单登录

如何让 MVC 动作返回 404

Spring MVC:如何在@ResponseBody 中返回图像?

如何在 Spring MVC 中使用 AJAX 渲染视图