使用 SasLocator 不显示上传的文件上传到 Azure 媒体服务/blob 存储

Upload to Azure Media Services / blob storage with SasLocator not showing uploaded file

所以我想将视频从客户端桌面应用程序上传到 Azure 媒体服务(当然使用 Azure 存储)。

我正在尝试进行以下组合:

第一个展示了我的场景的完美示例,但第二个展示了如何使用 BlobTransferClient 上传多个文件并有一个 "progress" 指示器。

问题:它似乎确实在上传,上传后我没有收到任何错误,但 Azure 门户/存储帐户中没有显示任何内容。 似乎正在上传,因为任务需要很长时间,任务管理器显示 wifi 上传进度,Azure 存储显示正在发出(成功的)请求。

所以,服务器端,我临时创建了一个 SasLocator:

public async Task<VideoUploadModel> GetSasLocator(string filename)
{

    var assetName = filename + DateTime.UtcNow;

    IAsset asset = await _context.Assets.CreateAsync(assetName, AssetCreationOptions.None, CancellationToken.None);

    IAccessPolicy accessPolicy = _context.AccessPolicies.Create(assetName, TimeSpan.FromMinutes(10),
        AccessPermissions.Write);

    var locator = _context.Locators.CreateLocator(LocatorType.Sas, asset, accessPolicy);

    var blobUri = new UriBuilder(locator.Path);
    blobUri.Path += "/" + filename;

    var model = new VideoUploadModel()
    {
        Filename = filename,
        AssetName = assetName,
        SasLocator = blobUri.Uri.AbsoluteUri,
        AssetId = asset.Id
    };
    return model;
}

和客户端,我尝试上传:

public async Task UploadVideoFileToBlobStorage(string[] files, string sasLocator, CancellationToken cancellationToken)
{
    var blobUri = new Uri(sasLocator);
    var sasCredentials = new StorageCredentials(blobUri.Query);

    //var blob = new CloudBlockBlob(new Uri(blobUri.GetComponents(UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.UriEscaped)), sasCredentials);
    var blobClient = new CloudBlobClient(new Uri(blobUri.GetComponents(UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.UriEscaped)), sasCredentials);
    var blobTransferClient = new BlobTransferClient(TimeSpan.FromMinutes(1))
    {
        NumberOfConcurrentTransfers = 2,
        ParallelTransferThreadCount = 2
    };
    //register events
    blobTransferClient.TransferProgressChanged += BlobTransferClient_TransferProgressChanged;
    //files
    var uploadTasks = new List<Task>();
    foreach (var filePath in files)
    {
        await blobTransferClient.UploadBlob(blobUri, filePath, new FileEncryption(), cancellationToken, blobClient, new NoRetry());
    }



    //StorageFile storageFile = null;

    //if (string.IsNullOrEmpty(file.FutureAccessToken))
    //{
    //    storageFile = await StorageFile.GetFileFromPathAsync(file.Path).AsTask(cancellationToken);
    //}
    //else
    //{
    //    storageFile = await StorageApplicationPermissions.FutureAccessList.GetFileAsync(file.FutureAccessToken).AsTask(cancellationToken);
    //}
    //cancellationToken.ThrowIfCancellationRequested();
    //await blob.UploadFromFileAsync(storageFile);
}

我知道我可能没有正确地命名资产和使用进度指示器而不是等待,但当然我首先希望它在完成之前先工作。

我将 Azure 媒体服务配置为 "Connect to Media Services API with service principal",我在其中创建了一个新的 Azure AD 应用程序并为其生成了密钥,例如 this documentation page。我不太确定这到底是如何工作的,我对 Azure AD 和 Azure AD 应用程序没有什么经验(指导?)。

正在上传:

已创建资产但没有文件:

存储也不显示任何文件:

存储确实显示上传成功:

我不能完全遵循 Upload multiple files with Media Services .NET SDK 文档的原因是因为它使用 _context(即 Microsoft.WindowsAzure.MediaServices.Client.CloudMediaContext),_context 我可以使用服务器端但不是客户端,因为它需要 TentantDomain、RESTAPI 端点、ClientId 和客户端密码。

我想通过 SaSLocator 上传是正确的方式 (?)。

更新 1

当使用 CloudBlockBlob 上传时,它会再次上传并显示在我的资产内的存储帐户中,但是当我进入 azure 中的媒体服务并单击特定资产时,它没有显示任何文件。

所以代码:

var blob = new CloudBlockBlob(new Uri(blobUri.GetComponents(UriComponents.SchemeAndServer | UriComponents.Path, UriFormat.UriEscaped)), sasCredentials);
//files
var uploadTasks = new List<Task>();
foreach (var filePath in files)
{
    await blob.UploadFromFileAsync(filePath, CancellationToken.None);
}

我还尝试在 Azure 中手动上传资产。因此,在资产菜单中单击 "Upload",然后对其进行编码。这一切都很好。

更新 2:

深入挖掘后,我提出了以下尚未经过生产验证的方法来使其目前有效:

1.直接从存储中获取共享访问签名并将其上传到那里:

public static async Task<string> GetMediaSasLocator(string filename)
{
    CloudBlobContainer cont = await GetMediaContainerAsync();
    SharedAccessBlobPolicy policy = new SharedAccessBlobPolicy()
    {
        SharedAccessExpiryTime = DateTimeOffset.UtcNow.AddMinutes(60),
        Permissions = SharedAccessBlobPermissions.Write,
        SharedAccessStartTime = DateTimeOffset.UtcNow.AddMinutes(-5)
    };
    await cont.FetchAttributesAsync();

    return cont.Uri.AbsoluteUri + "/" + filename + cont.GetSharedAccessSignature(policy);
}

有了这个 SaS,我可以像在更新 1 中展示的那样上传,没有任何改变。

2. 创建一个 Azure Function(已经计划这样做)来处理资产创建、文件上传到资产、编码和发布。 这是通过遵循本教程完成的:Visual Studio 的 Azure 函数工具,然后实现 Upload multiple files with Media Services .NET SDK 中说明的代码。

所以这个 "works" 但还不完美,我的客户端 WPF 应用程序中仍然没有进度指示器,而且 Azure Function 需要很长时间才能完成,因为我们基本上 "upload" 文件已在 Azure 存储中后再次添加到资产中。我宁愿使用一种方法从一个容器复制到资产容器。

我来到这里是因为 Azure 函数需要一个固定的给定容器名称,因为资产在存储帐户中创建自己的容器,您无法在这些容器上触发 Azure 函数。所以要使用 Azure Functions,我似乎真的必须将它上传到一个固定的容器名称,然后再做剩下的事情。

问题仍然存在: 为什么通过 BlobTransferClient 将视频文件上传到 Azure 存储不起作用?如果可行,我该如何触发基于多个容器的 Azure 函数。首选 'path',如 asset-{name}/{name}.avi。

最终我发现我需要在 UploadBlob 方法中指定 base URL,所以没有 SasLocator 中的文件名本身URL,但只有容器名称。

修复后我还注意到它没有上传到我在 SasLocator 中提供的文件名我生成的服务器端(它包含一个 customerID 前缀)。我不得不使用其他方法重载之一来获取正确的文件名。

public async Task UploadVideoFilesToBlobStorage(List<VideoUploadModel> videos, CancellationToken cancellationToken)
{
    var blobTransferClient = new BlobTransferClient();
    //register events
    blobTransferClient.TransferProgressChanged += BlobTransferClient_TransferProgressChanged;
    //files
    _videoCount = _videoCountLeft = videos.Count;
    foreach (var video in videos)
    {
        var blobUri = new Uri(video.SasLocator);
        //create the sasCredentials
        var sasCredentials = new StorageCredentials(blobUri.Query);
        //get the URL without sasCredentials, so only path and filename.
        var blobUriBaseFile = new Uri(blobUri.GetComponents(UriComponents.SchemeAndServer | UriComponents.Path,
            UriFormat.UriEscaped));
        //get the URL without filename (needed for BlobTransferClient (seems to me like a issue)
        var blobUriBase = new Uri(blobUriBaseFile.AbsoluteUri.Replace("/"+video.Filename, ""));

        var blobClient = new CloudBlobClient(blobUriBaseFile, sasCredentials);
        //upload using stream, other overload of UploadBlob forces to put online filename of local filename
        using (FileStream fs = new FileStream(video.FilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            await blobTransferClient.UploadBlob(blobUriBase, video.Filename, fs, null, cancellationToken, blobClient, 
                new NoRetry(), "video/x-msvideo");
        }
        _videoCountLeft -= 1;
    }

    blobTransferClient.TransferProgressChanged -= BlobTransferClient_TransferProgressChanged;
}

private void BlobTransferClient_TransferProgressChanged(object sender, BlobTransferProgressChangedEventArgs e)
{
    Console.WriteLine("progress, seconds remaining:" + e.TimeRemaining.Seconds);
    double bytesTransfered = e.BytesTransferred;
    double bytesTotal = e.TotalBytesToTransfer;
    double thisProcent = bytesTransfered / bytesTotal;
    double procent = thisProcent;
    //devide by video amount
    int videosUploaded = _videoCount - _videoCountLeft;
    if (_videoCountLeft > 0)
    {
        procent = (thisProcent + videosUploaded) / _videoCount;
    }

    procent = procent * 100;//to real %
    UploadProgressChangedEvent?.Invoke((int)procent, videosUploaded, _videoCount);
}

实际上Microsoft.WindowsAzure.MediaServices.Client.BlobTransferClient应该可以并发上传但是没有上传多个的方法但是它有NumberOfConcurrentTransfersParallelTransferThreadCount的属性,不知道如何使用这个。

我没有检查这是否也适用于资产,因为我现在为每个文件上传到 1 个容器,然后使用 Azure 函数处理资产,主要是因为我无法触发动态容器名称上的 Azure 函数(每个资产创建自己的容器)。