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