今天這篇文章用來闡述如何在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();
}
}
好了 當然我們還需要創建一些錯誤頁面來顯示,這裏我就不寫這些頁面的創建了,有興趣的可以下載源碼下來看看。
謝謝