.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)); }
疑问
- 针对仅授权用户可访问的视频,是否应像上述代码一样通过后端以MemoryStream传输?静态文件方案不可行,因为需校验权限。
- 前端服务器的工作原理是什么?(此问题次要)
- 现有代码的请求流程是哪种:
- (1) 用户点击播放→浏览器请求前端服务器→前端服务器全量获取后端视频→浏览器从前端服务器分块获取Blob文件?
- (2) 用户点击播放→浏览器直接请求后端接口→视频全量下载到浏览器内存后再分块处理?(核心差异是视频存储位置)
- 如果是流程(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
相关产品推荐
相关产品推荐

