如何使用Azure函数中的范围标头和部分内容来流式传输存储在Azure Blob存储中的mp4文件?

如何使用Azure函数中的范围标头和部分内容来流式传输存储在Azure Blob存储中的mp4文件?,azure,.net-core,azure-functions,azure-storage-blobs,Azure,.net Core,Azure Functions,Azure Storage Blobs,我想创建一个Azure函数,使用请求范围头和部分响应(http状态代码206s),将mp4文件从Azure Blob存储流式传输到客户端,如HTML视频标记。我正在使用Azure函数,以便以后可以实施所需的安全检查 FileStreamResult类可以自动处理来自html视频标记的范围请求,因此下面的操作可以很好地将视频从本地文件系统流到浏览器,允许用户随意向前/向后扫描视频: [FunctionName("StreamVideo")] public sta

我想创建一个Azure函数,使用请求范围头和部分响应(http状态代码206s),将mp4文件从Azure Blob存储流式传输到客户端,如HTML视频标记。我正在使用Azure函数,以便以后可以实施所需的安全检查

FileStreamResult类可以自动处理来自html视频标记的范围请求,因此下面的操作可以很好地将视频从本地文件系统流到浏览器,允许用户随意向前/向后扫描视频:

    [FunctionName("StreamVideo")]
    public static async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)]
        HttpRequest req, ILogger log)
    {
        // This stream is seekable!
        Stream str = File.OpenRead($@"C:\temp\BigBuckBunny.mp4");

        return new FileStreamResult(str, new MediaTypeHeaderValue("video/mp4").MediaType)
        {
            EnableRangeProcessing = true,
        };
    }
[FunctionName(“StreamVideo”)]
公共静态异步任务运行(
[HttpTrigger(AuthorizationLevel.Anonymous,“get”,Route=null)]
HttpRequest请求,ILogger日志)
{
//这条小溪是可以看到的!
Stream str=File.OpenRead($@“C:\temp\BigBuckBunny.mp4”);
返回新的FileStreamResult(str,新的MediaTypeHeaderValue(“video/mp4”).MediaType)
{
EnableRangeProcessing=true,
};
}
我想完全做到这一点,但要使用位于Azure Blob存储中的文件。以下代码不起作用。它在一个响应中返回整个文件,http状态代码为200。这大概是因为溪流不可寻

    [FunctionName("StreamVideo")]
    public static async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)]
        HttpRequest req, ILogger log)
    {
        string containerName = req.Query["container"];
        string blobName = req.Query["blob"];

        var blobServiceClient = new BlobServiceClient("CONNECTION_STRING_HERE", new BlobClientOptions(BlobClientOptions.ServiceVersion.V2019_12_12));

        BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(containerName);
        BlobClient blob = containerClient.GetBlobClient(blobName);

        // This stream is NOT seekable!
        Stream str = await blob.OpenReadAsync();

        return new FileStreamResult(str, new MediaTypeHeaderValue("video/mp4").MediaType)
        {
            EnableRangeProcessing = true,
        };
    }
[FunctionName(“StreamVideo”)]
公共静态异步任务运行(
[HttpTrigger(AuthorizationLevel.Anonymous,“get”,Route=null)]
HttpRequest请求,ILogger日志)
{
字符串containerName=req.Query[“container”];
字符串blobName=req.Query[“blob”];
var blobServiceClient=new blobServiceClient(“连接字符串”在这里),new blobclientions(blobclientions.ServiceVersion.V2019_12_12));
BlobContainerClient containerClient=blobServiceClient.GetBlobContainerClient(containerName);
BlobClient blob=containerClient.GetBlobClient(blobName);
//这条小溪是看不见的!
Stream str=await blob.OpenReadAsync();
返回新的FileStreamResult(str,新的MediaTypeHeaderValue(“video/mp4”).MediaType)
{
EnableRangeProcessing=true,
};
}

我怎样才能让它工作?请注意,我已经使用Web Api for.NET Framework 4.x实现了一个类似的解决方案,该解决方案将PushStreamContent与相同的媒体文件一起使用,因此我知道Blob存储中的mp4文件没有问题。我的函数以.NET Core 3.1为目标,目前正在本地对其进行测试。

我刚刚检查了您的代码,那里似乎没有错误。这里有些东西你可以查一下

1.导航至azure blob存储,并检查视频的内容类型是否为
video/mp4

2.对于
MediaTypeHeaderValue
名称空间,我使用的是:
使用System.Net.Http.Headers

3.以下是我使用的软件包:

<PackageReference Include="Azure.Storage.Blobs" Version="12.6.0" />
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.7" />

在我这边,我可以看到响应代码实际上是206。截图如下:


我最终使用以下解决方案实现了这一点。这导致my Azure函数产生206个响应,有效负载由
chunkSize
变量定义(在本例中为896.2kB)

[FunctionName(“StreamVideo”)]
公共静态异步任务运行(
[HttpTrigger(AuthorizationLevel.Anonymous,“get”,Route=null)]
HttpRequest请求,ILogger日志)
{
字符串containerName=req.Query[“container”];
字符串blobName=req.Query[“blob”];
var blobServiceClient=new blobServiceClient(“这里是连接字符串”,
新的BlobClient(BlobClient.ServiceVersion.V2019_12_12));
BlobContainerClient containerClient=blobServiceClient.GetBlobContainerClient(containerName);
BlobClient blob=containerClient.GetBlobClient(blobName);
响应blobPropertiesResponse=await blob.GetPropertiesAsync();
long blobFileLength=blobPropertiesResponse.Value.ContentLength;
长chunkSize=1000000;
字符串[]rangeHeaders=req.Headers[“Range”][0]。子字符串(6)。拆分(“-”;
long start=int.Parse(rangeHeaders[0]);
long end=string.IsNullOrEmpty(rangeHeaders[1])
?数学最小值(开始+块大小,blobFileLength-1)
:Math.Min(start+chunkSize,long.Parse(rangeHeaders[1]);
Stream=等待blob.OpenReadAsync(开始);
var httpResponseMessage=新httpResponseMessage
{
内容=新流内容(流),状态代码=HttpStatusCode.PartialContent,
};
httpResponseMessage.Content.Headers.ContentLength=end-start+1;
httpResponseMessage.Content.Headers.ContentType=新媒体类型HeaderValue(“视频/mp4”);
httpResponseMessage.Content.Headers.ContentRange=新的ContentRangeHeaderValue(开始、结束、blobFileLength);
httpResponseMessage.Headers.AcceptRanges.Add(“字节”);
httpResponseMessage.StatusCode=HttpStatusCode.PartialContent;
返回httpResponseMessage;
}
这是Firefox网络输出:


需要返回数据的是Azure函数。它在使用本地文件时工作,但是
EnableRangeProcessing
不喜欢从Azure返回的流(因为CanSeek==false)。我认为您需要创建一个可查找的流包装器。感谢您花时间。我有类型为video/mp4和application/octet-stream的文件,但我的函数总是以200响应返回整个文件。我尝试过使用与您相同的软件包版本,并且我为MediaTypeHeaderValue使用的名称空间是相同的。我可以确认你从你的职能部门得到206条回复吗?我的函数确实从Blob存储中获得206个响应,但我希望我的函数依次将206s返回给它的客户机。e、 g.
        [FunctionName("StreamVideo")]
        public static async Task<HttpResponseMessage> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)]
            HttpRequest req, ILogger log)
        {
            string containerName = req.Query["container"];
            string blobName = req.Query["blob"];

            var blobServiceClient = new BlobServiceClient("CONNECTION_STRING_HERE",
                new BlobClientOptions(BlobClientOptions.ServiceVersion.V2019_12_12));

            BlobContainerClient containerClient = blobServiceClient.GetBlobContainerClient(containerName);
            BlobClient blob = containerClient.GetBlobClient(blobName);

            Response<BlobProperties> blobPropertiesResponse = await blob.GetPropertiesAsync();
            long blobFileLength = blobPropertiesResponse.Value.ContentLength;

            long chunkSize = 1000000;
            string[] rangeHeaders = req.Headers["Range"][0].Substring(6).Split("-");
            long start = int.Parse(rangeHeaders[0]);
            long end = string.IsNullOrEmpty(rangeHeaders[1])
                ? Math.Min(start + chunkSize, blobFileLength - 1)
                : Math.Min(start + chunkSize, long.Parse(rangeHeaders[1]));

            Stream stream = await blob.OpenReadAsync(start);

            var httpResponseMessage = new HttpResponseMessage
            {
                Content = new StreamContent(stream), StatusCode = HttpStatusCode.PartialContent,
            };

            httpResponseMessage.Content.Headers.ContentLength = end - start + 1;
            httpResponseMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("video/mp4");
            httpResponseMessage.Content.Headers.ContentRange = new ContentRangeHeaderValue(start, end, blobFileLength);
            httpResponseMessage.Headers.AcceptRanges.Add("bytes");
            httpResponseMessage.StatusCode = HttpStatusCode.PartialContent;

            return httpResponseMessage;
        }