今天这篇文章用来阐述如何在Windows Azure的平台上管理应用程序文件权限,如你所知,在Azure平台上运行的程序不建议将用户的文件放在一台Server上面,因为Azure平台通常是多个实例同时运行的,而用户是不知道自己是在访问那一个instance(由load balancer自动分配)相互之间如果需要访问这些Server上的文件,必须通过internal endpoint来访问这些文件,通常你还需要对这些文件进行同步,即一个instance上面的用户文件更新了,你必须还要将这个更新分配给所有instance。
所以我们建议把数据放在Azure的Storage里面(也可以是SQL Azure),利用云存储来解决在多个instance之间数据共享的问题,而这将会带来一些问题,例如我们需要对某些数据进行保护而不是让所有人都能访问到,你可能会想到通过设置BlobContainer的权限来做到这一点,但是似乎为每个用户去申请一个Container似乎不是非常好,因为每个BlobContainer可以容纳100T的数据,比较浪费。所以这里我们通过这个示例来做到这一点,这里将会按照文件路径的形式来处理Blob文件,同时利用HttpModulerr来处理访问的文件。
[本示例完整源码下载(0分)] http://download.csdn.net/detail/aa466564931/4147108
针对于有些用户不知道如何开始运行这个程序,建议了解一下Azure Storage:
http://msdn.microsoft.com/en-us/library/windowsazure/ee924681.aspx
设置好你的连接字符串(连接到你的Azure账号,或者是本地测试的Storage emulator),并且必须将Azure项目设为启动项。
先决条件:
下载Azure SDK 1.6,并且保证已经安装了SQL Server express版本。
名称为CSAzureServeFilesFromBlobStorage, 并且选择建立一个Web Role。这里我们同样会使用Table Storage来管理Blob文件,如果我们需要查询功能,只需要访问这个Table便可以得到Blob的一些信息:
创建一个class library来处理Table Storage:
FileEntity 实体类:
public class FileEntity : TableServiceEntity
{
/// <summary>
/// No parameters constructor
/// </summary>
public FileEntity()
{
PartitionKey = "all";
RowKey = string.Format("{0:10}-{1}", DateTime.MaxValue.Ticks - DateTime.Now.Ticks, Guid.NewGuid()).Replace("-", "");
}
/// <summary>
/// With parameters constructor
/// </summary>
/// <param name="partitionKey"></param>
public FileEntity(string partitionKey)
{
PartitionKey = partitionKey;
RowKey = string.Format("{0:10}-{1}", DateTime.MaxValue.Ticks - DateTime.Now.Ticks, Guid.NewGuid()).Replace("-", "");
}
public string FileName
{
get;
set;
}
public string FileUrl
{
get;
set;
}
}
Context 类
public class FileContext : TableServiceContext
{
public FileContext(string baseAddress, StorageCredentials credentials)
: base(baseAddress, credentials)
{
}
/// <summary>
/// Get all entities of table storage "files".
/// </summary>
public IEnumerable<FileEntity> GetEntities
{
get
{
var list = this.CreateQuery<FileEntity>("files");
return list;
}
}
}
DataSource 类
public class FileDataSource
{
private static CloudStorageAccount account;
private FileContext context;
public FileDataSource()
{
// Create table storage client via cloud account.
account = CloudStorageAccount.FromConfigurationSetting("StorageConnections");
CloudTableClient client = account.CreateCloudTableClient();
client.CreateTableIfNotExist("files");
// Table context properties.
context = new FileContext(account.TableEndpoint.AbsoluteUri, account.Credentials);
context.RetryPolicy = RetryPolicies.Retry(3, TimeSpan.FromSeconds(1));
context.IgnoreResourceNotFoundException = true;
context.IgnoreMissingProperties = true;
}
/// <summary>
/// Get all entities method.
/// </summary>
/// <returns></returns>
public IEnumerable<FileEntity> GetAllEntities()
{
var list = from m in this.context.GetEntities
select m;
return list;
}
/// <summary>
/// Get table rows by partitionKey.
/// </summary>
/// <param name="partitionKey"></param>
/// <returns></returns>
public IEnumerable<FileEntity> GetEntities(string partitionKey)
{
var list = from m in this.context.GetEntities
where m.PartitionKey == partitionKey
select m;
return list;
}
/// <summary>
/// Get specify entity.
/// </summary>
/// <param name="partitionKey"></param>
/// <param name="fileName"></param>
/// <returns></returns>
public FileEntity GetEntitiesByName(string partitionKey, string fileName)
{
var list = from m in this.context.GetEntities
where m.PartitionKey == partitionKey && m.FileName == fileName
select m;
if (list.Count() > 0)
return list.First<FileEntity>();
else
return null;
}
/// <summary>
/// Add an entity.
/// </summary>
/// <param name="entity"></param>
public void AddFile(FileEntity entity)
{
this.context.AddObject("files", entity);
this.context.SaveChanges();
}
/// <summary>
/// Make a judgment to check if file is exists.
/// </summary>
/// <param name="filename"></param>
/// <param name="partitionKey"></param>
/// <returns></returns>
public bool FileExists(string filename, string partitionKey)
{
IEnumerable<FileEntity> list = from m in this.context.GetEntities
where m.FileName == filename && m.PartitionKey == partitionKey
select m;
if (list.Count()>0)
{
return true;
}
else
{
return false;
}
}
接着我们要在WebRole中创建一个HttpModuler来检测用户的访问和并且将禁止的request扔到错误处理的页面中去:
FileManagerModuler.cs
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(this.Application_BeginRequest);
}
/// <summary>
/// Check file types and request it by cloud blob API.
/// </summary>
/// <param name="source"></param>
/// <param name="e"></param>
private void Application_BeginRequest(Object source,EventArgs e)
{
string url = HttpContext.Current.Request.Url.ToString();
string fileName = url.Substring(url.LastIndexOf('/') + 1).ToString();
string extensionName = string.Empty;
if (fileName.Substring(fileName.LastIndexOf('.') + 1).ToString().Equals("aspx"))
{
return;
}
if (!fileName.Equals(string.Empty))
{
extensionName = fileName.Substring(fileName.LastIndexOf('.') + 1).ToString();
if (!extensionName.Equals(string.Empty))
{
string contentType = this.GetContentType(fileName);
if (contentType.Equals(string.Empty))
{
HttpContext.Current.Server.Transfer("NoHandler.aspx");
};
{
FileDataSource dataSource = new FileDataSource();
string paritionKey = this.GetPartitionName(extensionName);
if (String.IsNullOrEmpty(paritionKey))
{
HttpContext.Current.Server.Transfer("NoHandler.aspx");
}
FileEntity entity = dataSource.GetEntitiesByName(paritionKey, "Files/" + fileName);
if (entity != null)
HttpContext.Current.Response.Redirect(entity.FileUrl);
else
HttpContext.Current.Server.Transfer("NoResources.aspx");
}
}
}
}
/// <summary>
/// Get file's Content-Type.
/// </summary>
/// <param name="filename"></param>
/// <returns></returns>
public string GetContentType(string filename)
{
string res = string.Empty;
switch (filename.Substring(filename.LastIndexOf('.') + 1).ToString().ToLower())
{
case "jpg":
res = "image/jpg";
break;
case "css":
res = "text/css";
break;
}
return res;
}
/// <summary>
/// Get file's partitionKey.
/// </summary>
/// <param name="extensionName"></param>
/// <returns></returns>
public string GetPartitionName(string extensionName)
{
string partitionName = string.Empty;
switch(extensionName)
{
case "jpg":
partitionName = "image";
break;
case "css":
partitionName = "css";
break;
}
return partitionName;
}
并且把HttpModule注册到Web.config文件中,因为WebRole是部署到IIS7.0的,所以必须放在System.WebServer下:
<system.webServer>
<modules runAllManagedModulesForAllRequests="true" >
<add name="ServeFilesFromBlobStorageWebRole.FileManagerModuler" type="ServeFilesFromBlobStorageWebRole.FileManagerModuler" />
</modules>
</system.webServer>
最后制作一个上传的功能:
/// <summary>
/// Upload existing resources. ("Files" folder)
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
protected void btnUpload_Click(object sender, EventArgs e)
{
FileDataSource source = new FileDataSource();
List<string> nameList = new List<string>() { "Files/Microsoft.jpg", "Files/MSDN.jpg", "Files/Site.css" };
CloudBlobClient client = account.CreateCloudBlobClient();
CloudBlobContainer container = client.GetContainerReference("container");
container.CreateIfNotExist();
var permission = container.GetPermissions();
permission.PublicAccess = BlobContainerPublicAccessType.Container;
container.SetPermissions(permission);
bool flag = false;
foreach (string name in nameList)
{
if (name.Substring(name.LastIndexOf('.') + 1).Equals("jpg") && File.Exists(Server.MapPath(name)))
{
if (!source.FileExists(name, "image"))
{
flag = true;
CloudBlob blob = container.GetBlobReference(name);
blob.UploadFile(Server.MapPath(name));
FileEntity entity = new FileEntity("image");
entity.FileName = name;
entity.FileUrl = blob.Uri.ToString();
source.AddFile(entity);
lbContent.Text += String.Format("The image file {0} is uploaded successes. <br />", name);
}
}
else if (name.Substring(name.LastIndexOf('.') + 1).Equals("css") && File.Exists(Server.MapPath(name)))
{
if (!source.FileExists(name, "css"))
{
flag = true;
CloudBlob blob = container.GetBlobReference(name);
blob.UploadFile(Server.MapPath(name));
FileEntity entity = new FileEntity("css");
entity.FileName = name;
entity.FileUrl = blob.Uri.ToString();
source.AddFile(entity);
lbContent.Text += String.Format("The css file {0} is uploaded successes. <br />", name);
}
}
}
if (!flag)
{
lbContent.Text = "You had uploaded these resources";
}
}
然后大家通过以下代码来显示现有的文件,这里我添加两个不存在和无效的链接,以示区别:
private static CloudStorageAccount account;
public string embedString = string.Empty;
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
account = CloudStorageAccount.FromConfigurationSetting("StorageConnections");
FileDataSource source = new FileDataSource();
IEnumerable<FileEntity> list = source.GetAllEntities();
StringBuilder sb = new StringBuilder();
if (list.Count() > 0)
{
sb.Append("Then please check them: <br />");
foreach (FileEntity entity in list)
{
sb.Append("<a href='File/");
sb.Append(entity.FileName);
sb.Append("' target='_blank'>");
sb.Append(entity.FileName);
sb.Append("</a>");
sb.Append("<br />");
}
}
sb.Append("<a href='HTMLSamoke.htm' target='_blank' >HTML resource (No available)</a><br />");
sb.Append("<a href='HTMLSamoke.jpg' target='_blank' >HTML resource (No resources)</a>");
embedString = sb.ToString();
}
}
好了 当然我们还需要创建一些错误页面来显示,这里我就不写这些页面的创建了,有兴趣的可以下载源码下来看看。
谢谢