You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Spring Boot(WebFlux)和Angular实现视频流并解决接口响应异常问题

问题定位

后端问题

  • @RequestHeader("Range") 默认必填,浏览器首次请求视频资源不会携带Range头,会直接返回400错误
  • 仅接收Range参数但未做分片逻辑处理,直接返回完整Resource,无法正确响应206分片请求,视频拖动进度条等操作会失效
  • 若项目打包为jar包,classpath下的资源无法通过普通Resource读取流的方式正常获取(可选注意点)

前端问题

  • Angular HttpClient默认响应类型为json,你未指定responseType: 'blob',会将返回的二进制视频流当做JSON解析,直接触发解析错误
  • 泛型指定为[]和实际返回的二进制类型完全不匹配
  • 拿到的视频流未经过Angular安全策略处理,直接赋值给[src]会被安全拦截器拦截,无法正常加载

解决办法

后端代码修正

import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourceRegion;

// 类内其他代码
private static String FORMAT  = "classpath:videos/%s.mp4";

@Autowired
private ResourceLoader resourceLoader;

@GetMapping(value = "video/{title}", produces = "video/mp4")
public Mono<ResourceRegion> getVideos(@PathVariable String title, 
                                      @RequestHeader(value = "Range", required = false) String range) throws IOException {
    Resource resource = resourceLoader.getResource(String.format(FORMAT, title));
    // 分片大小可自定义,这里设为1MB
    long chunkSize = 1024 * 1024;
    long contentLength = resource.contentLength();
    if (range == null) {
        // 无Range头返回第一块
        return Mono.just(new ResourceRegion(resource, 0, Math.min(chunkSize, contentLength)));
    }
    // 解析Range头,格式为 bytes=start-end
    String[] ranges = range.replace("bytes=", "").split("-");
    long start = Long.parseLong(ranges[0]);
    long end = ranges.length > 1 && !ranges[1].isEmpty() ? Long.parseLong(ranges[1]) : Math.min(start + chunkSize, contentLength - 1);
    long regionLength = end - start + 1;
    return Mono.just(new ResourceRegion(resource, start, regionLength));
}

前端代码修正

1. 服务层代码修改

getVideo(title: string): Observable<Blob> {
  return this.http
    .get(BASIC_URL + `api/video/${title}`, {
      headers: this.createRange(),
      responseType: 'blob' // 关键:指定响应类型为二进制blob
    })
    .pipe(
      tap((_) => this.log('Getting video chunks')),
      catchError(this.handleError<Blob>('Error getting video', new Blob()))
    );
}

2. 组件层代码修改

需引入DomSanitizer处理安全URL:

import { DomSanitizer, SafeUrl } from '@angular/platform-browser';

// ...组件内代码
videoInByte: SafeUrl;

constructor(private sanitizer: DomSanitizer, private videoService: VideoService) {}

loadVideo(title: string) {
  this.videoService.getVideo(title).subscribe(blob => {
    const blobUrl = URL.createObjectURL(blob);
    // 绕过安全策略校验
    this.videoInByte = this.sanitizer.bypassSecurityTrustUrl(blobUrl);
  });
}

3. HTML代码无需修改即可正常使用

<div class="container">
    <h4>Spring Boot Video streaming</h4>
    <video [src]='videoInByte' width="700px" height="400px" controls  preload="none">
    </video>
</div>

可选优化:如果接口无鉴权,可直接给video的src赋值为接口地址,无需手动发起请求,浏览器会自动处理Range分片逻辑:<video [src]="BASIC_URL + 'api/video/xxx'" ...>


内容的提问来源于stack exchange,提问作者Syed Saad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 02:15:00