开启跨域访问Blob
前面我们了解了如何通过共享访问签名让客户端无需存储账号密钥直接访问Blob存储,对大多数客户端来说,共享访问签名是实现对Windows Azure Blob存储读/写访问的全部要求,但对Silverlight来说,有一个跨域访问限制,下面我们介绍如何通过Silverlight开启对Blob的无限制访问。
ClientAccessPolicy.xml
当Silverlight程序产生一个跨域调用时,首先要从目标服务器的根取得一个叫做 ClientAccessPolicy.xml 的文件,在我们的例子中,我们的URL是 http://slupload.blob.core.windows.net/… ,因此Silverlight会尝试访问 http://slupload.blob.core.windows.net/ClientAccessPolicy.xml 策略文件。
Windows Azure存储中的每个Blob都驻留在容器中,有一个特殊的根容器允许我们离开根域直接存储Blob, ClientAccessPolicy.xml 就放在这里,下面的代码创建了一个公共的可读的根容器,并在它里面创建了一个名为 ClientAccessPolicy.xml 的Blob。
{
blobs.GetContainerReference("$root").CreateIfNotExist();
blobs.GetContainerReference("$root").SetPermissions(
new BlobContainerPermissions() {
PublicAccess = BlobContainerPublicAccessType.Blob
});
var blob = blobs.GetBlobReference("clientaccesspolicy.xml");
blob.Properties.ContentType = "text/xml";
blob.UploadText(@"<?xml version=""1.0"" encoding=""utf-8""?>
<access-policy>
<cross-domain-access>
<policy>
<allow-from http-methods=""*"" http-request-headers=""*"">
<domain uri=""*"" />
<domain uri=""http://*"" />
</allow-from>
<grant-to>
<resource path=""/"" include-subpaths=""true"" />
</grant-to>
</policy>
</cross-domain-access>
</access-policy>");
}
关于 ClientAccessPolicy.xml 有几个重要的事情需要提醒一下:
1、Blob有个 text/xml 内容类型,我发现在某些浏览器中,Silverlight不能接受含有错误内容类型的 ClientAccessPolicy.xml 。
2、我们使用 allow-from http-methods 增加除GET和POST HTTP动词以外的支持。
3、我们使用 allow-from http-request-headers 允许自定义消息头,支持与 x-ms-version 类似的消息头。
4、我们使用 domain uri=”http://*” 允许非SSL客户端基于HTTPS访问Blob存储。
使用Silverlight客户端HTTP堆栈处理
默认情况下,Silverlight使用浏览器HTTP堆栈产生Web请求,它限制了你只能使用GET和POST动词,从Silverlight 3开始,提供了客户端HTTP堆栈,可以使用其它HTTP动词了,如我们创建Blob时要用到的PUT。
为了确保我们使用客户端HTTP堆栈,需要使用 WebRequestCreator.ClientHttp.Client() 方法构造我们的Web请求。
在Silverlight中创建Blob
Windows Azure SDK中的.Net存储客户端库不能用于Silverlight(主要是HTTP堆栈稍微有点不同),因此我们需要自己实现创建Blob的方法,幸运的是,适用于Blob存储的REST API相当简单。下面的Silverlight代码在一个Blob中存储了文本“ Hello World ”(假设URI已经附加了适当的共享访问签名)。
webRequest.Method = "PUT";
webRequest.ContentType = "text/plain";
webRequest.BeginGetRequestStream((ar) =>
{
using (var writer = new StreamWriter(webRequest.EndGetRequestStream(ar)))
{
writer.WriteLine("Hello, World!");
}
webRequest.BeginGetResponse((ar2) =>
{
((HttpWebRequest)ar2.AsyncState).EndGetResponse(ar2);
}, null);
}, null);