javascript - 使用 Angular 从安全端点流式传输视频

标签 javascript c# angular jwt video-streaming

我有安全的端点。我需要在来自 Angular 的 Http Get 请求的头部传递一个 jwt token 来流式传输视频。
dotnet 核心 Controller 中的端点如下所示(简化):

[Route("api/GetVideo/{videoId}")]
public async Task<IActionResult> GetVideoAsync(int videoId)
{
    string videoFilePath = GiveMeTheVideoFilePath(videoId);  

    return this.PhysicalFile(videoFilePath, "application/octet-stream", enableRangeProcessing: true);
}
Angular 代码:
video.component.html
<video width="100%" height="320" crossorigin="anonymous" controls #videoElement>
        <source
            [src]="'http://mydomain/api/GetVideo/1' | authVideo | async"  type="application/octet-stream"
        />
</video>
video.pipe.ts
@Pipe({
    name: 'authVideo',
})
export class AuthVideoPipe implements PipeTransform {
    constructor(private http: HttpClient, private auth: AuthTokenService) {}

    transform(url: string) {
        return new Observable<string>(observer => {
            const token = this.auth.getToken(); //this line gets the jwt token
            const headers = new HttpHeaders({ Authorization: `Bearer ${token}` });

            const { next, error } = observer;

            return this.http.get(url, { headers, responseType: 'blob' }).subscribe(response => {
                const reader = new FileReader();
                reader.readAsDataURL(response);
                reader.onloadend = function() {
                    observer.next(reader.result as string);
                };
            });
        });
    }
}
它确实使用上述代码向端点发出 get 请求。一些东西返回到前端。但是视频没有播放。我从这个SO question找到了上面的方法.它适用于图像,但显然不适用于视频。我的想法是我可能需要在前端逐字节读取流。如果是这样,我该怎么做?
我尝试在两端将“application/octet-stream”更改为“video/mp4”。但没有运气。
请注意,当我从后端删除安全代码并从 html 中删除 authVideo 管道时,它可以正常工作。请给我一些启示。谢谢!

最佳答案

虽然此解决方案适用于图像,因为所有数据都已加载 一次作为数据 URL,您不应该对视频执行此操作,因为它会禁用浏览器的流式传输功能。事实上,通过这样做,您正在加载整个视频 内存中在转换成数据URL之前,这在性能和用户体验方面确实很糟糕(需要在播放之前加载完整的视频,并且会导致大量内存消耗)。
您的问题的明显解决方案是使用 cookie 进行身份验证:

  • 验证成功时设置 cookie
  • 它会在后续请求中自动发回,包括视频一

  • 在下文中,我假设您由于某种原因不能这样做。
    您可以使用 MediaSource .它允许您控制为检索视频而发送的实际请求(并添加 Authorization header )。
    请注意,即使所有浏览器都广泛支持它,这也是实验性的。
    您的代码应如下所示:
    assetURL = 'http://mydomain/api/GetVideo/1';
    
    // Modify this with the actual mime type and codec
    mimeCodec = 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"';
    
    @ViewChild("videoElement") video: ElementRef;
    
    ngAfterViewInit() {
      if (
        "MediaSource" in window &&
        MediaSource.isTypeSupported(this.mimeCodec)
      ) {
        const mediaSource = new MediaSource();
        (this.video.nativeElement as HTMLVideoElement).src = URL.createObjectURL(
          mediaSource
        );
        mediaSource.addEventListener("sourceopen", () =>
          this.sourceOpen(mediaSource)
        );
      } else {
        console.error("Unsupported MIME type or codec: ", this.mimeCodec);
      }
    }
    
    sourceOpen(mediaSource) {
      const sourceBuffer = mediaSource.addSourceBuffer(this.mimeCodec);
      const token = this.auth.getToken(); //this line gets the jwt token
      const headers = new HttpHeaders({ Authorization: `Bearer ${token}` });
      return this.http
        .get(this.assetURL, { headers, responseType: "blob" })
        .subscribe(blob => {
          sourceBuffer.addEventListener("updateend", () => {
            mediaSource.endOfStream();
            this.video.nativeElement.play();
          });
          blob.arrayBuffer().then(x => sourceBuffer.appendBuffer(x));
        });
    }
    
    这个working demo给出以下结果:
    Request headers

    关于javascript - 使用 Angular 从安全端点流式传输视频,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63200611/

    相关文章:

    C# 将列表加载到文本框 WinForms

    c# - 与另一个列表 LINQ 相比,检查一个列表中有哪些元素

    javascript - 为什么 `document.getElementById(“#datepicker1” )` 找不到我的元素?

    javascript - 如何将参数从javascript发送到css

    javascript - Django 模板 onclick 内联 html javascript 替代

    c# - 为什么 Task.Delay 在这种情况下不起作用

    html - 页面加载期间显示的 Angular 验证

    javascript - Angular 2 : property becomes underfined

    Angular 10 : Unable to write a reference

    javascript - 闭包是否必须有一个外部函数,或者它可以仅引用一个外部作用域?