Spring Boot实现HTML5视频流控制器端点的正确方式咨询
实现Spring Boot HTML5视频源控制器端点的正确方式
嘿,我来帮你搞定这个Spring Boot媒体服务器的视频端点问题!你的基础思路是对的,但还有几个关键细节需要完善,才能让HTML5视频播放器正常工作(比如支持拖拽播放、异常处理这些)。
一、基础完善版实现
首先补全你未写完的代码,同时加上必要的校验和异常处理:
import org.springframework.core.io.FileSystemResource; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.io.File; @RestController public class VideoController { // 假设你的视频文件存储在这个目录下,实际可以配置到application.properties里 private static final String VIDEO_STORAGE_PATH = "/path/to/your/videos/"; @GetMapping(value = "/videosrc", produces = "video/mp4") public ResponseEntity<FileSystemResource> videoSource(@RequestParam(value = "id", required = true) int id) { // 根据id拼接视频文件路径,这里假设文件名是id.mp4,你可以根据自己的规则调整 File videoFile = new File(VIDEO_STORAGE_PATH + id + ".mp4"); // 校验文件是否存在且可读 if (!videoFile.exists() || !videoFile.canRead()) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } return ResponseEntity.ok(new FileSystemResource(videoFile)); } }
二、关键优化:支持范围请求(Range Requests)
HTML5视频播放器默认会发送Range请求头来分段加载视频(比如只请求视频的第0-1024字节),这样用户可以拖拽进度条。如果你的端点不支持Range请求,视频可能只能从头播放,没法拖拽。
Spring Boot已经内置了ResourceHttpMessageConverter来处理这种场景,但如果需要手动自定义逻辑,可以这样实现:
import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestHeader; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.io.File; import java.io.RandomAccessFile; import java.nio.file.Files; @RestController public class VideoController { private static final String VIDEO_STORAGE_PATH = "/path/to/your/videos/"; @GetMapping(value = "/videosrc", produces = MediaType.APPLICATION_OCTET_STREAM_VALUE) public ResponseEntity<byte[]> videoSource( @RequestParam(value = "id", required = true) int id, @RequestHeader(value = "Range", required = false) String rangeHeader) throws Exception { File videoFile = new File(VIDEO_STORAGE_PATH + id + ".mp4"); if (!videoFile.exists() || !videoFile.canRead()) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } long fileLength = videoFile.length(); if (rangeHeader == null) { // 没有Range请求,返回完整视频 HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(fileLength)); headers.add(HttpHeaders.CONTENT_TYPE, "video/mp4"); return new ResponseEntity<>(Files.readAllBytes(videoFile.toPath()), headers, HttpStatus.OK); } // 处理Range请求 String[] rangeParts = rangeHeader.replace("bytes=", "").split("-"); long start = Long.parseLong(rangeParts[0]); long end = rangeParts.length > 1 ? Long.parseLong(rangeParts[1]) : fileLength - 1; if (start >= fileLength) { return new ResponseEntity<>(HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE); } end = Math.min(end, fileLength - 1); long contentLength = end - start + 1; byte[] content = new byte[(int) contentLength]; try (RandomAccessFile raf = new RandomAccessFile(videoFile, "r")) { raf.seek(start); raf.read(content); } HttpHeaders headers = new HttpHeaders(); headers.add(HttpHeaders.CONTENT_RANGE, "bytes " + start + "-" + end + "/" + fileLength); headers.add(HttpHeaders.CONTENT_LENGTH, String.valueOf(contentLength)); headers.add(HttpHeaders.CONTENT_TYPE, "video/mp4"); return new ResponseEntity<>(content, headers, HttpStatus.PARTIAL_CONTENT); } }
三、额外性能优化:流式输出大视频
如果你的视频文件很大,直接返回byte[]会占用大量内存,推荐使用StreamingResponseBody来流式输出,避免内存溢出:
import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.mvc.method.annotation.StreamingResponseBody; import java.io.File; import java.nio.file.Files; @RestController public class VideoController { private static final String VIDEO_STORAGE_PATH = "/path/to/your/videos/"; @GetMapping(value = "/videosrc", produces = "video/mp4") public ResponseEntity<StreamingResponseBody> streamVideo(@RequestParam int id) throws Exception { File videoFile = new File(VIDEO_STORAGE_PATH + id + ".mp4"); if (!videoFile.exists()) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } StreamingResponseBody responseBody = outputStream -> { Files.copy(videoFile.toPath(), outputStream); }; return ResponseEntity.ok() .contentType(MediaType.parseMediaType("video/mp4")) .contentLength(videoFile.length()) .body(responseBody); } }
这样实现后,HTML5的<video>标签就可以直接通过src="/videosrc?id=1"来加载视频了,而且支持拖拽、分段加载这些核心功能。
内容的提问来源于stack exchange,提问作者user9585140
相关产品推荐
相关产品推荐

