You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

.NET Core 2.0 Web API基于FileStream实现视频流的技术问询

Got it, let's tackle this scenario for your .NET Core Web API. Since you need to stream videos to a <video> tag without exposing direct file paths, and handle dynamic sources with async processing, here's a practical implementation that supports standard video streaming features like range requests (which browsers use for seeking):

.NET Core Web API Video Streaming Implementation (Async, No Direct File Paths)

Core Approach

We'll build an API endpoint that:

  • Handles range requests (required for smooth video playback and seeking)
  • Asynchronously fetches video sources (easily swappable if your file storage changes later)
  • Streams content back with proper HTTP headers to work seamlessly with the <video> tag

Step 1: Controller Action with Full Range Support

Here's a complete async controller action that works for both full video requests and partial range requests (sent by browsers when users skip timestamps):

using Microsoft.AspNetCore.Mvc;
using System.IO;
using System.Net;
using System.Threading.Tasks;

[ApiController]
[Route("api/[controller]")]
public class VideoStreamController : ControllerBase
{
    // This is your dynamic video source handler — swap this logic when your file source changes
    private async Task<Stream> GetVideoSourceStreamAsync(string videoId)
    {
        // Example 1: Load from a local file (async mode)
        var localFilePath = $"your/internal/storage/path/{videoId}.mp4";
        return new FileStream(localFilePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize: 4096, useAsync: true);

        // Example 2: Load from cloud storage (e.g., Azure Blob — uncomment to use)
        // var blobClient = _blobServiceClient.GetBlobContainerClient("videos").GetBlobClient($"{videoId}.mp4");
        // return await blobClient.OpenReadAsync();
    }

    [HttpGet("{videoId}")]
    public async Task<IActionResult> StreamVideo(string videoId)
    {
        using var videoStream = await GetVideoSourceStreamAsync(videoId);
        var totalFileLength = videoStream.Length;

        // Handle range requests (sent by browsers for seeking)
        if (Request.Headers.TryGetValue("Range", out var rangeHeader))
        {
            var range = ParseRangeHeader(rangeHeader, totalFileLength);
            if (range == null)
            {
                return StatusCode((int)HttpStatusCode.RequestedRangeNotSatisfiable);
            }

            var (startByte, endByte) = range.Value;
            var segmentLength = endByte - startByte + 1;

            videoStream.Seek(startByte, SeekOrigin.Begin);

            // Set required range headers
            Response.Headers.Add("Content-Range", $"bytes {startByte}-{endByte}/{totalFileLength}");
            Response.Headers.Add("Accept-Ranges", "bytes");
            Response.Headers.Add("Content-Length", segmentLength.ToString());

            return new FileStreamResult(videoStream, "video/mp4")
            {
                EnableRangeProcessing = false, // We handle range logic manually here
                StatusCode = (int)HttpStatusCode.PartialContent
            };
        }
        else
        {
            // Full video request: send the entire file
            Response.Headers.Add("Content-Length", totalFileLength.ToString());
            return new FileStreamResult(videoStream, "video/mp4")
            {
                EnableRangeProcessing = true
            };
        }
    }

    // Helper to parse the Range header (e.g., "bytes=0-1023" or "bytes=500-")
    private (long Start, long End)? ParseRangeHeader(string rangeHeader, long totalFileLength)
    {
        var rangeParts = rangeHeader.Replace("bytes=", "").Split('-');
        if (!long.TryParse(rangeParts[0], out var startByte))
        {
            return null;
        }

        var endByte = rangeParts.Length > 1 && long.TryParse(rangeParts[1], out var parsedEnd)
            ? parsedEnd
            : totalFileLength - 1;

        if (startByte > endByte || startByte >= totalFileLength)
        {
            return null;
        }

        endByte = Math.Min(endByte, totalFileLength - 1);
        return (startByte, endByte);
    }
}

Step 2: Key Details for Your Use Case

  • Async Source Abstraction: The GetVideoSourceStreamAsync method is your "switch" for video sources. If you later move files to cloud storage or a database, you only need to update this method — no changes to the streaming logic required.
  • Range Request Support: Browsers rely on Range headers to request specific parts of the video (like when a user jumps to the middle). This implementation handles those requests natively, so your <video> tag will support seeking out of the box.
  • No Exposed File Paths: Clients only interact with the /api/videostream/{videoId} endpoint — your actual file storage path is never shared publicly.

Step 3: Frontend Integration

Point your <video> tag directly to the API endpoint:

<video controls width="800">
    <source src="/api/videostream/my-unique-video-id" type="video/mp4">
    Your browser does not support HTML5 video.
</video>

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

火山引擎 最新活动