66 lines
2.5 KiB
Java
66 lines
2.5 KiB
Java
package com.guwan.backend.controller;
|
|
|
|
import com.guwan.backend.entity.Video;
|
|
import com.guwan.backend.service.HdfsStorageService;
|
|
import lombok.RequiredArgsConstructor;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import org.springframework.core.io.InputStreamResource;
|
|
import org.springframework.core.io.ResourceRegion;
|
|
import org.springframework.http.*;
|
|
import org.springframework.web.bind.annotation.*;
|
|
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
|
|
@Slf4j
|
|
@RestController
|
|
@RequestMapping("/api/stream")
|
|
@RequiredArgsConstructor
|
|
public class VideoStreamController {
|
|
|
|
private final HdfsStorageService hdfsStorageService;
|
|
private static final long CHUNK_SIZE = 1024 * 1024; // 1MB chunks
|
|
|
|
@GetMapping("/video/{id}")
|
|
public ResponseEntity<ResourceRegion> streamVideo(
|
|
@PathVariable Long id,
|
|
@RequestHeader(value = "Range", required = false) String rangeHeader) {
|
|
try {
|
|
// 获取视频路径
|
|
Video video = videoMapper.selectById(id);
|
|
if (video == null) {
|
|
return ResponseEntity.notFound().build();
|
|
}
|
|
|
|
// 获取视频流
|
|
InputStream videoStream = hdfsStorageService.getVideoStream(video.getUrl());
|
|
InputStreamResource resource = new InputStreamResource(videoStream);
|
|
|
|
// 获取视频元数据
|
|
VideoMetadata metadata = hdfsStorageService.getVideoMetadata(video.getUrl());
|
|
long contentLength = metadata.getSize();
|
|
|
|
// 处理Range请求
|
|
HttpRange range = rangeHeader == null ? null : HttpRange.parseRanges(rangeHeader).get(0);
|
|
ResourceRegion region;
|
|
|
|
if (range != null) {
|
|
long start = range.getRangeStart(contentLength);
|
|
long end = range.getRangeEnd(contentLength);
|
|
long rangeLength = Math.min(CHUNK_SIZE, end - start + 1);
|
|
region = new ResourceRegion(resource, start, rangeLength);
|
|
} else {
|
|
long rangeLength = Math.min(CHUNK_SIZE, contentLength);
|
|
region = new ResourceRegion(resource, 0, rangeLength);
|
|
}
|
|
|
|
return ResponseEntity.status(HttpStatus.PARTIAL_CONTENT)
|
|
.contentType(MediaTypeFactory.getMediaType(resource).orElse(MediaType.APPLICATION_OCTET_STREAM))
|
|
.body(region);
|
|
|
|
} catch (IOException e) {
|
|
log.error("视频流处理失败", e);
|
|
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
|
|
}
|
|
}
|
|
} |