基于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将媒体文件上传到媒体服务包含许多步骤,主要流程是:
- 创建资产(ASSET)
- 对资产进行加密(可选项)—本文暂时不予以考虑
- 将媒体文件上传到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 |
或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",
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 |
或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 |
"AssetId" : “<AssetId>”,
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 |
|
Request Content Type |
|
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按钮现在也可以使用了,之后便可以进行后续媒体的操作如编码,将在后续文章中详细讲述,敬请期待。