前端使用video标签去请求后端视频数据,后端使用springboot搭建的,由于视频文件太大导致请求不了,有没有人给个解决方案😭
由于视频文件比较大,直接将其全部加载到前端是不现实的。一种解决方案是使用流式传输,将视频文件分成多个数据块逐步传输,并在前端内存中进行拼接和播放。
下面提供一种基于 Range 请求和分块传输的解决方案:
在后端中,可以通过实现 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 中返回给客户端。
在前端中,可以将视频数据通过 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 服务器和客户端播放器。