基于Windows Azure Media Service REST API 进行Windows Store/Windows Phone 应用开发系列-Part 3上传媒体到WAMS

在文章 基于Windows Azure Media Service REST API 进行Windows Store/Windows Phone 应用开发系列-Part 1 简介中介绍了基于Windows Azure Media Service(Windows Azure 媒体服务,简称WAMS)进行应用开发的相关信息及包含的基本流程,紧接着在文章基于Windows Azure Media Service REST API 进行Windows Store/Windows Phone 应用开发系列-Part 2初始设置链接到WAMS中介绍了如何使用REST API链接到媒体服务,本文将基于此对如何上传媒体到媒体服务进行详细讲解。

使用REST API将媒体文件上传到媒体服务包含许多步骤,主要流程是:

  1. 创建资产(ASSET)
  2. 对资产进行加密(可选项)—本文暂时不予以考虑
  3. 将媒体文件上传到blob storage

请注意,在使用REST API时访问媒体服务时,必须在HTTP请求中添加如下必须的header;WAMS 的根 URI 为https://media.windows.net/,成功连接到此 URI后,会收到一个“301 重定向”响应并提取出新的媒体服务URI,随后调用新都是基于该 URI,详细内容参见Part2内容。

根据文档媒体服务 REST API 开发的设置可知,每次调用WAMS时,客户端必须在请求中包括必需的标头,列表如下:

Header

Type

Value

Authorization

Bearer

Bearer 是唯一接受的授权机制。

该值还必须包括由 ACS 提供的访问令牌。

x-ms-version

Decimal

2.7

DataServiceVersion

Decimal

3.0

MaxDataServiceVersion

Decimal

3.0

 如Part 1中描述:资产(ASSET)是包含媒体信息的逻辑实体,可能包含了一个或多个需要处理的数字文件如audio,video等。资产实体是对资产的抽象,包含了一系列的属性如Id, State等,为便于处理,我们定义Asset类用于表示媒体服务中的一个资产:

创建资产:在媒体服务中创建一个新的资产

EndPoint

https://media.windows.net/API/Assets

或redirection后的新的URI/Assets

HTTP Method

POST

Request Headers

DataServiceVersion: 3.0MaxDataServiceVersion: 3.0x-ms-version: 2.7Authorization: Bear + ACSToken

Request Content Type

application/json;odata=verbose

Request Body Format

{ "Name": "<Asset Name>", (通常使用媒体文件名,本文省略加密操作)format>"}e.g.{ "Name": "<filename>", (此处常用媒体文件的名字)}

 

通常根据媒体文件名创建asset, 如下代码根据文件名在媒体服务中创建相应的资产实体,代码执行成功后可以提取相应的Id值(做为AssetId)如nb:cid:UUID:636363d9-7c66-42ca-add4-0a8c4d4464f6。

(为便于描述,本文先假设上传位于媒体库中名为interview3.wmv的文件,以Part2链接媒体服务的内容为基础)

private string AssetId;

private string AccessPolicyId;

private string UploadUrl;

private string UploadEndpoint;

private static readonly string mediaFileName = "interview3.wmv"; 

private async Task<string> CreateAssetAsync(string acsToken, string wamsEndPoint, string mediaName)

        { 

            AuthenticationHeaderValue header = CreateBasicAuthenticationHeader(acsToken); 

            HttpClient httpClient = new HttpClient();

            httpClient.MaxResponseContentBufferSize = int.MaxValue; 

            httpClient.DefaultRequestHeaders.Add("Authorization", header.ToString());

            httpClient.DefaultRequestHeaders.Add("x-ms-version", "2.7");

            httpClient.DefaultRequestHeaders.Add("DataServiceVersion", "3.0");

            httpClient.DefaultRequestHeaders.Add("MaxDataServiceVersion", "3.0"); 

            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, wamsEndPoint);

            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));          

            string assetCreateRequestPayloadFormat = @"{0} ""Name"": {1} {2} {3} {4}";

            string requestBody = string.Format(CultureInfo.InvariantCulture, assetCreateRequestPayloadFormat, "{","\"", mediaName,"\"", "}");    

            HttpContent body = new StringContent(requestBody, Encoding.UTF8, "application/json");         

            var response = await httpClient.PostAsync(wamsEndPoint + "Assets", body); 

            string assetId = null;

            if (response.IsSuccessStatusCode)

            {

                var content = await response.Content.ReadAsStringAsync();

                XmlDocument xmlDoc = new XmlDocument();

                xmlDoc.LoadXml(content); 

                XmlNodeList elemList = xmlDoc.GetElementsByTagName("d:Id");

                foreach (var ele in elemList)

                {

                    assetId = ele.InnerText;

                }     

            }

            return assetId;

        }

此时再去创建该资产下的资产文件,获取资产实体下对应的资产文件的ID, 以便后续过程中将上传的媒体文件更新到该ID下(MergeAssetFilesAsync):

   private async Task<string> CreateAssetFilesAsync(string acsToken, string wamsEndPoint, string assetId, string mediaFileName)

        {

            AuthenticationHeaderValue header = CreateBasicAuthenticationHeader(acsToken);

            HttpClient httpClient = new HttpClient();

            httpClient.MaxResponseContentBufferSize = int.MaxValue;

            httpClient.DefaultRequestHeaders.Add("Authorization", header.ToString());

            httpClient.DefaultRequestHeaders.Add("x-ms-version", "2.7");

            httpClient.DefaultRequestHeaders.Add("DataServiceVersion", "3.0");

            httpClient.DefaultRequestHeaders.Add("MaxDataServiceVersion", "3.0");

            String requestBody = "{ \"Name\" : \"" + mediaFileName + "\"," +

                                  " \"ContentFileSize\" : \"" + "0" + "\", " +

                                  " \"ParentAssetId\" : \"" + assetId + "\", " +

                                  " \"MimeType\" : \"" + "video/x-ms-wmv" + "\"}";

            HttpContent body = new StringContent(requestBody, Encoding.UTF8, "application/json");     

            var response = await httpClient.PostAsync(wamsEndPoint + "Files", body);

            string assetFileId = null;

            if (response.IsSuccessStatusCode)

            {

                var content = await response.Content.ReadAsStringAsync();

                XmlDocument xmlDoc = new XmlDocument();

                xmlDoc.LoadXml(content);

                XmlNodeList elemList = xmlDoc.GetElementsByTagName("d:Id");

                foreach (var ele in elemList)

                {

                    assetFileId = ele.InnerText;

                }           

            }

           return assetFileId;

        }

Part 1中描述,每个WAMS 账户都有一个或多个相关联的Azure Storage 账户,用于存储关联的WAMS 账户控制的媒体内容,在将媒体文件上传至Blob Storage时要求对资产设置相应的写入权限,然后提取用于上传的Storage URL,最后使用 Azure Storage REST APIs进行传输。

将任何文件上载到 BLOB 存储之前,需要设置用于对资产执行写入操作的访问策略权限。为此,需要向 AccessPolicy 实体集发送一个 HTTP POST 请求,创建AccessPolicy 请求简要总结如下:

EndPoint

 https://media.windows.net/API/AccessPolicies  

或redirection后的新的URI/AccessPolicies

HTTP Method

POST

Request Headers

DataServiceVersion: 3.0MaxDataServiceVersion: 3.0x-ms-version: 2.7Authorization: Bear + ACSToken

Request Content Type

application/json;odata=verbose

Request Body Format

{ "Name": "NewUploadPolicy",

   "DurationInMinutes" : "300",

     "Permissions" : 2

format>"}

 执行过程可参考如下代码, 此处是写的权限(Permissions=”2”),在 文章5媒体的发布过程中,同样需要指定资产“读”的权限(Permissions=”1”),执行成功后,根据返回内容获得相应的AccessPolicyId, 如:"nb:pid:UUID:58fcf51e-5219-4fea-8536-6c295d2c388a"。 

       /// <summary>

        ///apName

        /// for upload-NewUploadPolicy

        /// for download - DownloadPolicy

        /// accessType 2- upload(write), 1- read

        /// </summary>

        /// <param name="acsToken"></param>

        /// <param name="wamsEndPoint"></param>

        /// <param name="policyName"></param>

        /// <returns></returns>

        private async Task<string> CreateAccessPolicyAsync(string acsToken, string wamsEndPoint, string apName, int accessType)

        {

            AuthenticationHeaderValue header = CreateBasicAuthenticationHeader(acsToken);

            HttpClient httpClient = new HttpClient();

            httpClient.MaxResponseContentBufferSize = int.MaxValue;

            httpClient.DefaultRequestHeaders.Add("Authorization", header.ToString());

            httpClient.DefaultRequestHeaders.Add("x-ms-version", "2.7");

            httpClient.DefaultRequestHeaders.Add("DataServiceVersion", "3.0");

            httpClient.DefaultRequestHeaders.Add("MaxDataServiceVersion", "3.0");

            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, wamsEndPoint);

            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));         

            String requestBody = "{ \"Name\" : \"" + apName + "\"," +

                                   " \"DurationInMinutes\" : \"" + "300" + "\", " +

                                   " \"Permissions\" : " + accessType.ToString() + "}";

            HttpContent body = new StringContent(requestBody, Encoding.UTF8, "application/json");

            var response = await httpClient.PostAsync(wamsEndPoint + "AccessPolicies", body);

            string accessPolicyId = null;

            if (response.IsSuccessStatusCode)

            {

                var content = await response.Content.ReadAsStringAsync();

                XmlDocument xmlDoc = new XmlDocument();

                xmlDoc.LoadXml(content);

                XmlNodeList elemList = xmlDoc.GetElementsByTagName("d:Id");

                foreach (var ele in elemList)

                {

                    accessPolicyId = ele.InnerText;

                }  

            }

            return accessPolicyId;

        }

 

创建 AccessPolicy 后,将该Id链接到某个定位符实体,该实体将为你提供用于将文件上载到 BLOB 存储的 URL 路径,创建上传URL的请求简要总结如下:

EndPoint

 https://media.windows.net/API/Locators  

或redirection后的新的URI/Locators

HTTP Method

POST

Request Headers

DataServiceVersion: 3.0MaxDataServiceVersion: 3.0x-ms-version: 2.7Authorization: Bear + ACSToken

Request Content Type

application/json;odata=verbose

Request Body Format

 {     " AccessPolicyId": “< AccessPolicyId>”,

    "AssetId" : “<AssetId>”,

     "StartTime" : < DateTime.UtcNow.AddMinutes(-5) >”,(必须为YYYY-MM-DDTHH:mm:ss 格式)
      “Type”: “<1>”

format>"}

e.g:

{"AccessPolicyId" : "nb:pid:UUID:58fcf51e-5219-4fea-8536-6c295d2c388a", "AssetId" : "nb:cid:UUID:636363d9-7c66-42ca-add4-0a8c4d4464f6", "StartTime" : "2014-11-28T10:39:58", 

"Type" : 1

}

 执行过程可参考如下代码,执行成功后,根据返回内容构造相应的上传URL,注意必须将要上载的文件的文件名添加到在获取得到的URL中,如:https://myappsstorage.blob.core.windows.net/asset-636363d9-7c66-42ca-add4-0a8c4d4464f6/azure.wmv?sv=2012-02-12&sr=c&si=8561f64e-0392-47f6-a178-8397f6ee4352&sig=JjDaBdvxHqWsA%2FWrLMLkAqQjl92v03vwdOrqVIAjkV8%3D&st=2014-11-28T10%3A39%3A58Z&se=2014-11-28T15%3A39%3A58Z

  private async Task CreateUploadURLAsync(string acsToken, string wamsEndPoint, string accessPolicyId, string assetId)

        { 

            AuthenticationHeaderValue header = CreateBasicAuthenticationHeader(acsToken);

            HttpClient httpClient = new HttpClient();

            httpClient.MaxResponseContentBufferSize = int.MaxValue;

            httpClient.DefaultRequestHeaders.Add("Authorization", header.ToString());

            httpClient.DefaultRequestHeaders.Add("x-ms-version", "2.7");

            httpClient.DefaultRequestHeaders.Add("DataServiceVersion", "3.0");

            httpClient.DefaultRequestHeaders.Add("MaxDataServiceVersion", "3.0");

            HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, wamsEndPoint);

            request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); 

            String requestBody = "{ \"AccessPolicyId\" : \"" + accessPolicyId + "\"," +

                           " \"AssetId\" : \"" + assetId+ "\", " +

                           " \"StartTime\" : \"" + DateTime.UtcNow.AddMinutes(-5).ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ss") + "\", " +  

                           " \"Type\" : " + "1" + "}";     

            HttpContent body = new StringContent(requestBody, Encoding.UTF8, "application/json");

            var response = await httpClient.PostAsync(wamsEndPoint + "Locators", body);

            if (response.IsSuccessStatusCode)

            {

                var content = await response.Content.ReadAsStringAsync();

                XmlDocument xmlDoc = new XmlDocument();

                xmlDoc.LoadXml(content);

                XmlNodeList elemListBase = xmlDoc.GetElementsByTagName("d:BaseUri");               

                XmlNodeList elemListSV = xmlDoc.GetElementsByTagName("d:ContentAccessComponent");

                string baseUrl=null;

                string sv=null;

                foreach (var ele in elemListBase)

                {

                    baseUrl = ele.InnerText;

                }

                foreach (var ele in elemListSV)

                {

                    sv = ele.InnerText;

                }

                UploadUrl = baseUrl + "/" +Path.GetFileName(MediaFileName) +  sv;              

                UploadEndpoint = baseUrl.Substring(0, baseUrl.IndexOf("/asset"));

            }     

        } 

获得上传地址且设置好访问权限后,即可使用存储 REST API 将具体的文件上载到 Azure BLOB 存储容器,上传文件请求简要总结如下:

EndPoint

上传地址(blob storage SAS 定位符)

HTTP Method

PUT

Request Headers

 x-ms-version: 2011-08-18
 x-ms-date: 2011-01-17
 x-ms-blob-type: BlockBlob

Request Content Type

 application/octet-stream

Request Body Format

{  媒体文件二进制数据   

}

 执行过程可参考如下代码,需要注意的是二进制文件的添加,此处仅以本地Video库(Windows.Storage.KnownFolders.VideosLibrary)中的一个名为interview3.wmv媒体文件为例,可以做相应修改,如让用户选择要上传的文件(OpenFilePicker)或自带摄像头拍摄的视频,执行成功返回201创建成功。

  private async Task<bool> UploadFileAsync(string acsToken, string uploadEndPoint,string uploadUrl)

        {

            HttpClientHandler handler = new HttpClientHandler { MaxRequestContentBufferSize = 10000000 };

            HttpClient httpClient = new HttpClient(handler);       

            httpClient.DefaultRequestHeaders.Add("x-ms-version", "2011-08-18");

            httpClient.DefaultRequestHeaders.Add("x-ms-date", "2011-01-17");

            httpClient.DefaultRequestHeaders.Add("x-ms-blob-type", "BlockBlob");    

            StorageFolder library = Windows.Storage.KnownFolders.VideosLibrary;          

            var videoFile = await library.GetFileAsync(MediaFileName);

            var props = await videoFile.GetBasicPropertiesAsync();

            var stream = await videoFile.OpenStreamForReadAsync();

            ContentSize = props.Size;

            StreamContent streamContent = new StreamContent(stream, (int)props.Size);

            HttpResponseMessage response = await httpClient.PutAsync(uploadUrl, streamContent);

            if (response.IsSuccessStatusCode)

            {  

                return true;

            }

            else

            {

                 return false;

            }

        }

 

请注意到这一步为止,媒体文件已经上传至Blob Storage,但是还没有更新到我们的资产实体中,需要再发送一个更新请求才能完成整个媒体文件的上传工作,相应的执行过程可参考如下代码。

private async Task MergeAssetFilesAsync(string acsToken, string wamsEndPoint,string assetId, string assetFileId, string mediaFileName)

        {

            AuthenticationHeaderValue header = CreateBasicAuthenticationHeader(acsToken);

            String requestBody = "{ \"Name\" : \"" + mediaFileName + "\"," +

                                  " \"ContentFileSize\" : \"" + ContentSize + "\", " +

                                  " \"ParentAssetId\" : \"" + assetId + "\", " +

                                  " \"Id\" : \"" + assetFileId + "\", " +

                                   " \"MimeType\" : \"" + "video/x-ms-wmv" + "\"}"; 

            var request = (HttpWebRequest)HttpWebRequest.Create(wamsEndPoint + "Files('" + assetFileId + "')");

            request.Method = "MERGE";

            request.ContentType = "application/json;odata=verbose";

            request.Accept = "application/json;odata=verbose";

            request.Headers["DataServiceVersion"] = "3.0";

            request.Headers["MaxDataServiceVersion"] = "3.0";

            request.Headers["x-ms-version"] = "2.7";

            request.Headers["Authorization"] = header.ToString();

            var requestBytes = Encoding.UTF8.GetBytes(requestBody);

            var requestStream = await request.GetRequestStreamAsync();

            await requestStream.WriteAsync(requestBytes, 0, requestBytes.Length);

            await requestStream.FlushAsync();

            var response = await request.GetResponseAsync();

        }

 

完成文件的上传后,我们可以在Portal上看到该文件的大小,同时在页面底部的Encode按钮现在也可以使用了,之后便可以进行后续媒体的操作如编码,将在后续文章中详细讲述,敬请期待。