获取 java.io.IOException:在作为多部分文件流流式传输的视频中导航时发生管道异常异常

Getting java.io.IOException: Broken pipe exception when navigating in a video that is being streamed as a multi part file stream

我正在玩一个简单的视频流 spring 引导应用程序,使用 this github 存储库中的 MultipartFileSender class。由于它是一个非常大的文件,我不想将它包含在这里,但如果需要我可以包含它。

我的控制器如下:

package org.murat.test.controllers;

import org.murat.test.Utils.MultipartFileSender;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;

@RestController
@RequestMapping("/")
public class VideoController {
    protected final Logger logger = LoggerFactory.getLogger(this.getClass());

    @RequestMapping(value = "video", method = RequestMethod.GET)
    public void getVideo(HttpServletRequest request, HttpServletResponse response){
        File file = new File("/test-videos/BigBuckBunny.mp4");
        try {
            logger.debug("Streaming file '" + file.getName() + "'...");
            MultipartFileSender.fromFile(file)
                    .with(request)
                    .with(response)
                    .serveResource();
        } catch (Exception e) {
            String errorMessage = e.getLocalizedMessage();
            logger.error(errorMessage, e);
        }
    }
}

当我转到 URL http://hostname:8080/video 时,一切都运行完美,我可以在视频中播放、暂停、倒带和导航。

此时我唯一担心的是,每次我在视频中导航时(即使我开始播放)我都会遇到 org.apache.catalina.connector.ClientAbortException: java.io.IOException: Broken pipe 异常。

我做了一些研究,发现如果用户在服务器试图为用户提供服务时关闭网站或他们的连接,就会发生这种情况。所以在我的情况下发生这种情况是因为当我拖动到另一个框架时:

  1. 视频暂停。
  2. 转到所需的帧。
  3. 视频从 运行 开始 帧.

我假设此时原始连接丢失并创建了另一个连接。

我的问题是:

  1. 我能否在不出现此错误的情况下优雅地在连接之间切换?
  2. 如果不是,我可以安全地忽略这个错误吗(也许强制它 当我捕获到这个特定异常时将其显示为 warning/debug)?

问题可能是您不支持字节范围请求。大多数(如果不是全部)视频播放器都会期待这一点。它们可能会优雅地降级以使用常规(非字节范围)请求,但我怀疑播放器是否会完全正常运行。

Spring Content 支持开箱即用的视频流(字节范围)。使用 Spring Content FS(即 Spring 文件系统的内容),您将能够为自己创建一个由文件系统支持的商店,将您的视频放入该商店,并使用 Spring 内容 REST 通过 HTTP 为他们提供服务。

通过 start.spring.io 或通过您的 IDE spring 项目向导创建一个新的 Spring Boot 项目(撰写本文时 Spring Boot 1.5.10 ).添加以下 Spring 内容依赖项,以便您最终得到这些:-

<dependencies>
    <!-- Standard Spring Boot -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.3</version>
    </dependency>

    <!-- Spring Content -->
    <dependency>
        <groupId>com.github.paulcwarren</groupId>
        <artifactId>spring-content-fs-boot-starter</artifactId>
        <version>0.0.9</version>
    </dependency>
    <dependency>
        <groupId>com.github.paulcwarren</groupId>
        <artifactId>spring-content-rest-boot-starter</artifactId>
        <version>0.0.9</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

在您的 Spring 启动应用程序 class 中,创建一个 VideoStore。将其注释为 Store REST 资源。这会导致 Spring Content 注入(此接口的)实现以及 REST 端点,使您不必编写任何代码(即上面显示的控制器代码加上一些):-

    @SpringBootApplication 
    public class DemoApplication {

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

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

默认情况下 Spring 内容将在 java.io.tmpdir 下创建一个商店。因此,您还需要将 SPRING_CONTENT_FS_FILESYSTEM_ROOT 环境变量设置为指向 "store" 的根目录。

将您的视频复制到此 "root" 位置。启动应用程序,您的视频将可以从以下位置流式传输:-

/test-videos/BigBuckBunny.mp4

如果您不使用 Spring 引导,请告诉我,我可以 post 一个非 spring 引导示例。