简体   繁体   English

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

[英]How do I return a video with Spring MVC so that it can be navigated using the html5 <video> tag?

If I have a file in the web server (Tomcat) and create a tag, I can watch the video, pause it, navigate through it, and restart it after it finishes.如果我在 Web 服务器 (Tomcat) 中有一个文件并创建了一个标签,我就可以观看视频、暂停它、浏览它,并在它完成后重新启动它。

But if I create a REST interface that sends the video file when requested, and add its URL to a tag, I can only play and pause.但是,如果我创建一个 REST 接口在请求时发送视频文件,并将其 URL 添加到标签,我只能播放和暂停。 No rewinding, no fast forward, no navigating , nothing.没有倒带,没有快进,没有导航,什么都没有。

So, is there a way for this to be fixed?那么,有没有办法解决这个问题? Am I missing something somewhere?我在某处遗漏了什么吗?

Video files are in the same server as the REST interface , and the REST interface only checks session and sends the video after finding out which one it should send.视频文件和REST接口在同一台服务器上,REST接口只检查会话并在找到应该发送的视频后发送视频。

These are the methods I've tried so far.这些是我迄今为止尝试过的方法。 They all work, but none of them allow navigating.它们都可以工作,但都不允许导航。

Method 1, ResponseEntity:方法一,ResponseEntity:

/*
 * 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;
}

Method 2, Stream copy:方法二、Stream复制:

/*
 * 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());
    }
}

Method 3, FileSystemResource:方法三、FileSystemResource:

@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);
}

A simple solution for handling non-static resources:处理非静态资源的简单解决方案:

@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);
        }
    }
}

(inspired by Spring Boots LogFileMvcEndpoint and more or less equal to Paul-Warrens (@paul-warren) StoreByteRangeHttpRequestHandler which I found later on). (受 Spring Boots LogFileMvcEndpoint 的启发,或多或少等于我后来发现的 Paul-Warrens (@paul-warren) StoreByteRangeHttpRequestHandler )。

Hopefully this is something which Spring will support in the near future, see https://jira.spring.io/browse/SPR-13834 (please vote for it).希望这是 Spring 在不久的将来会支持的东西,请参阅https://jira.spring.io/browse/SPR-13834 (请投赞成票)。

The HTTP resume download function might be your friend. HTTP 恢复下载功能可能是您的朋友。 I had the same problem before.我以前也遇到过同样的问题。 After implementing http range the navigation in the video was possible:实现 http 范围后,视频中的导航是可能的:

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

I know this is an old post but in case it is useful to anyone else out there asking the same/similar questions.我知道这是一篇旧帖子,但以防万一它对其他提出相同/类似问题的人有用。

Now-a-days there are projects like Spring Content that natively support video streaming.现在有像Spring Content这样的项目本身就支持视频流。 All the code you would need for the simplest implementation would be:-最简单的实现所需的所有代码是:-

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

And this would be enough to create a Java API and a set of REST endpoints that would allow you to PUT/POST, GET and DELETE streams of video.这足以创建一个 Java API 和一组 REST 端点,允许您 PUT/POST、GET 和 DELETE 视频流。 GET support byte ranges and will play properly in HTML5 video players and such like. GET 支持字节范围,可以在 HTML5 视频播放器等中正常播放。

In fact, it is the front end who shows the video controls for the <video> tag.实际上,显示 <video> 标签的视频控件的是前端。

Each browser for a special video format, has a default control panel.每个浏览器对于一种特殊的视频格式,都有一个默认的控制面板。

You can use html and css to create your own control with media API.您可以使用 html 和 css 通过媒体 API 创建您自己的控件。 Media api媒体接口

From Div into HTML5 [By default, the <video> element will not expose any sort of player controls.Div 到 HTML5 [默认情况下,<video> 元素不会公开任何类型的播放器控件。 You can create your own controls with plain old HTML, CSS, and JavaScript.您可以使用普通的旧 HTML、CSS 和 JavaScript 创建自己的控件。 The <video> element has methods like play() and pause() and a read/write property called currentTime. <video> 元素具有诸如 play() 和 pause() 之类的方法以及名为 currentTime 的读/写属性。 There are also read/write volume and muted properties.还有读/写音量和静音属性。 So you really have everything you need to build your own interface.]因此,您确实拥有构建自己的界面所需的一切。]

To support Safari, you must handle the range requests and provide the proper 206 return code.要支持 Safari,您必须处理范围请求并提供正确的 206 返回码。 https://tools.ietf.org/html/rfc7233 https://tools.ietf.org/html/rfc7233

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM