前后端大型视频文件传输

前端使用video标签去请求后端视频数据,后端使用springboot搭建的,由于视频文件太大导致请求不了,有没有人给个解决方案😭

由于视频文件比较大,直接将其全部加载到前端是不现实的。一种解决方案是使用流式传输,将视频文件分成多个数据块逐步传输,并在前端内存中进行拼接和播放。

下面提供一种基于 Range 请求和分块传输的解决方案:

  1. 后端 Spring Boot 实现

在后端中,可以通过实现 Range 请求和分块传输等技术,将视频数据分成多个数据块逐步传输到前端。

示例代码:

@GetMapping("/video")
public void getVideo(HttpServletResponse response, @RequestParam("filePath") String filePath) throws IOException {
    File file = new File(filePath);
    long fileSize = file.length();

    String range = request.getHeader("Range");
    if (range == null) {
        response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
        return;
    }

    String[] rangeStr = range.split("-");
    long rangeStart = Long.parseLong(rangeStr[0].substring(6));
    long rangeEnd = rangeStart + 1024 * 1024; // 每次传输 1MB 数据

    if (rangeEnd >= fileSize) {
        rangeEnd = fileSize - 1;
    }

    long contentLength = rangeEnd - rangeStart + 1;
    response.setHeader("Content-Range", "bytes " + rangeStart + "-" + rangeEnd + "/" + fileSize);
    response.setHeader("Content-Length", String.valueOf(contentLength));
    response.setContentType("video/mp4");

    FileInputStream inputStream = new FileInputStream(file);
    inputStream.skip(rangeStart);

    OutputStream outputStream = response.getOutputStream();
    byte[] buffer = new byte[1024];
    int readBytes = 0;
    int totalReadBytes = 0;

    while (totalReadBytes < contentLength && (readBytes = inputStream.read(buffer)) != -1) {
        outputStream.write(buffer, 0, readBytes);
        totalReadBytes += readBytes;
    }

    outputStream.flush();
    inputStream.close();
    outputStream.close();
}

在上述示例中,使用 Range 请求获取客户端所需的数据块范围,然后使用 FileInputStream 按需读取数据,并将数据写入到 OutputStream 中返回给客户端。

  1. 前端实现

在前端中,可以将视频数据通过 AJAX 的方式进行获取。使用 HTML5 video 标签,将上述获取视频的 AJAX 返回数据作为 src,即可进行分块播放。

示例代码:

const videoElement = document.createElement('video');
document.body.appendChild(videoElement);
videoElement.controls = true;

let mediaSource = new MediaSource();
videoElement.src = URL.createObjectURL(mediaSource);

mediaSource.addEventListener('sourceopen', function() {
    let mediaSourceBuffer = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.42E01E, mp4a.40.2"');

    let rangeStart = 0;
    let rangeEnd = 1024 * 1024 - 1;

    const requestVideoData = function(startByte, endByte) {
        let xhr = new XMLHttpRequest();
        xhr.open("GET", "/getVideo?filePath=your_video_file_path");
        xhr.setRequestHeader("Range", "bytes=" + startByte + "-" + endByte);
        xhr.responseType = "arraybuffer";
        xhr.addEventListener("load", function() {
            let data = new Uint8Array(xhr.response);
            mediaSourceBuffer.appendBuffer(data);
            rangeStart = rangeEnd + 1;
            rangeEnd = rangeStart + 1024 * 1024 - 1;
            requestVideoData(rangeStart, rangeEnd);
        });
        xhr.send();
    };

    requestVideoData(rangeStart, rangeEnd);
});

在上述示例中,使用 MediaSource API 以及上面的 AJAX 请求获取数据并向 Media Source Buffer 中追加,每次追加完毕后,继续请求下一块数据,直到视频数据全部传输完毕。

以上就是一种基于 Range 请求和分块传输的解决方案,可以一定程度上解决视频文件过大无法加载的问题。需要注意的是,实现分块传输时需要兼顾资源占用和速度的平衡,以避免将服务器资源消耗殆尽。


除了基于 Range 请求和分块传输的方案,还可以使用常见的视频流媒体技术,如 HLS、DASH、RTMP 等。这些技术可以将视频流分成较小的分片,并使用自适应码率调整使其适应不同的带宽和终端设备,从而提高视频播放的稳定性和用户体验。

下面简要介绍一下这些技术:

HTTP Live Streaming (HLS)
HLS 是苹果推出的一种视频流传输协议,将视频分成较小的分片(通常为 10 秒左右),并使用 M3U8 文件描述视频的结构和分片信息。客户端可以根据网络带宽和设备能力来选择合适的清晰度和码率,实现优化的视频流传输。

Dynamic Adaptive Streaming over HTTP (DASH)
DASH 是一种基于 HTTP 的流媒体传输协议,与 HLS 类似,将视频分成较小的分片并使用 MPD 文件描述视频的结构和分片信息。客户端可以根据网络带宽和设备能力来选择合适的清晰度和码率,实现自适应码率调整。

Real Time Messaging Protocol (RTMP)
RTMP 是一种基于流媒体的传输协议,通常用于在线视频直播和实时数据传输。RTMP 可以实现低延迟、高质量的音视频传输,但需要使用专门的 RTMP 服务器和客户端播放器。