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

.NET Core后端向React前端流式传输大视频的技术问询

大视频传输技术问题(React + .NET Core 7.0)

我正在开发一款前端基于React、后端基于.NET Core 7.0的在线学习Web应用,遇到了大视频传输的技术难题。场景为已授权用户需观看1小时、约30GB的大视频,目前仅测试了1分钟的小视频,可正常播放,但对以下问题存在疑问:

测试代码

React前端测试代码

const [videoUrl, setVideoUrl] = useState<string>();
axiosPrivate
        .get(`/SomeVideoService/Video?id=${id}`, { responseType: "blob" })
        .then((response) => {
          const videoBlobUrl = URL.createObjectURL(response.data);
          setVideoUrl(videoBlobUrl);
        })
        .catch((error) => {
          console.error("Error fetching video:", error);
        });
return (
    <div>
      <video width="640" height="360" controls>
        <source src={videoUrl} type="video/mp4" />
        Your browser does not support the video tag.
      </video>
    </div>
  );

.NET Core后端测试代码

[HttpGet("Video")]
[Authorize(Roles = "FirstRole, SecondRole, ThirdRole")]
public async Task<IActionResult> GetVideo(string id)
{
    var video = "video.mp4";
    var filePath = Path.Combine(Directory.GetCurrentDirectory().ToString(), "Videos", video);

    if (!System.IO.File.Exists(filePath))
    {
        return NotFound();
    }
    var memory = new MemoryStream();
    using (var stream = new FileStream(filePath, FileMode.Open))
    {
        stream.CopyTo(memory);
    }
    memory.Position = 0;

    return File(memory, "video/mp4", Path.GetFileName(filePath));
}

疑问

  1. 针对仅授权用户可访问的视频,是否应像上述代码一样通过后端以MemoryStream传输?静态文件方案不可行,因为需校验权限。
  2. 前端服务器的工作原理是什么?(此问题次要)
  3. 现有代码的请求流程是哪种:
    • (1) 用户点击播放→浏览器请求前端服务器→前端服务器全量获取后端视频→浏览器从前端服务器分块获取Blob文件?
    • (2) 用户点击播放→浏览器直接请求后端接口→视频全量下载到浏览器内存后再分块处理?(核心差异是视频存储位置)
  4. 如果是流程(2),是否需要为后端Get接口添加Range请求头支持(如下列代码)?或是需要将大视频切割为10秒左右的小文件,再配合对应的前端代码传输?

带Range头的后端参考代码

[HttpGet("Video")]
[Authorize(Roles = "Common, Extended")]
public async Task<IActionResult> GetVideo(string id)
{
    var video = "video.mp4";
    var filePath = Path.Combine(Directory.GetCurrentDirectory(), "Videos", video);
    if (!System.IO.File.Exists(filePath))
    {
        return NotFound();
    }

    var fileInfo = new FileInfo(filePath);
    long fileLength = fileInfo.Length;
    var lastModified = fileInfo.LastWriteTimeUtc;
    var entityTag = new EntityTagHeaderValue($"\"{fileInfo.LastWriteTimeUtc.Ticks}\"");

    var requestHeaders = Request.GetTypedHeaders();
    var responseHeaders = Response.GetTypedHeaders();
    responseHeaders.CacheControl = new CacheControlHeaderValue
    {
        Public = true,
        MaxAge = TimeSpan.FromSeconds(3600)
    };
    responseHeaders.LastModified = lastModified;
    responseHeaders.ETag = entityTag;

    if (requestHeaders.IfModifiedSince.HasValue && requestHeaders.IfModifiedSince.Value >= lastModified)
    {
        return StatusCode(StatusCodes.Status304NotModified);
    }

    if (requestHeaders.IfNoneMatch != null && requestHeaders.IfNoneMatch.Contains(entityTag))
    {
        return StatusCode(StatusCodes.Status304NotModified);
    }

    var range = requestHeaders.Range;
    if (range == null || !RangeHelper.TryGetRangeItem(range, fileLength, out var rangeItem))
    {
        return File(System.IO.File.OpenRead(filePath), "video/mp4", enableRangeProcessing: false);
    }

    var start = rangeItem.From ?? 0;
    var end = rangeItem.To ?? (fileLength - 1);

    var contentLength = end - start + 1;
    var contentRange = new ContentRangeHeaderValue(start, end, fileLength);

    Response.StatusCode = StatusCodes.Status206PartialContent;
    responseHeaders.ContentRange = contentRange;

    var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
    fileStream.Seek(start, SeekOrigin.Begin);

    return File(fileStream, "video/mp4", enableRangeProcessing: true, fileDownloadName: null, lastModified, entityTag, contentLength);
}

切割视频对应的前端参考代码

import React, { useState, useEffect } from 'react';

const VideoPlayer = ({ videoId }) => {
  const [videoUrl, setVideoUrl] = useState('');

  useEffect(() => {
    const apiUrl = `SomeVideoService/Video${videoId}`;
    setVideoUrl(apiUrl);
  }, [videoId]);

  if (!videoId) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <video width="640" height="360" controls>
        <source src={videoUrl} type="video/mp4" />
        Your browser does not support the video tag.
      </video>
    </div>
  );
};

export default VideoPlayer;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 20:07:33