技术开发 频道

【第10篇】使用Blob Storage搭建简单网络硬盘

  StorageClient项目的结构如下图。

 

  该 Project 中分别定义了三个 Windows Azure Storage 服务的抽象类(如BlobStorage.cs),同时定义了与抽象类吻合的封装 REST 实现方法的类(如RestBlobStorage.cs)。RestBlobStorage 就是本文中将要使用的明星类。


  以下是关键步骤(拖控件之类的简单步骤就不示范了,请参考文末附件中的详细代码)。

  第一步:
 
  在新建 VS008 中新建 Web Cloud Service。配置好 ServiceConfiguration.cscfg、ServiceDefinition.csdef 和 WebRole 下的 Web.Config 文件。具体方法见Azure Services Platform Step by Step-(8) 开发部署Azure留言板。

 

 

  第二步:

  在 WebRole 项目中添加对项目的引用。项目可以在 Azure SDK 中找到。本文篇末附件里也可以直接下载。

  第三步:

  在 Page 中定义 BlobContainer 类型的私有变量。

  private BlobContainer container;BlobContainer 封装了所有有关 Blob 的方法,可以说是非常强大,如下图:

 

  第四步:

  从配置文件中读取有关 Blob 账户、Container 名称的配置,新建 Container,传入 BlobContainer 的实体.

private BlobContainer GetContainer()
{
BlobStorage blobStorage
=    BlobStorage.Create(StorageAccountInfo.GetDefaultBlobStorageAccountFromConfiguration());
BlobContainer container
= blobStorage.GetBlobContainer(ConfigurationManager.AppSettings["containerName"]);
container.CreateContainer(
null, ContainerAccessControl.Public);
return container;
}

  这步可以看成是初始化 BlobContainer 实体的操作。

  第五步:

  在 Blob 中写入文件的方法。

private void SaveFileToCloud(string id, string description, string fileName,    string contentType, byte[] data,string size)
{
//BlobProperties类,顾名思义,Blob的属性集
BlobProperties properties =    new BlobProperties(string.Format(CultureInfo.InvariantCulture, "file_{0}", id));
//每个Blob都可以附带一些自定义属性(Metadata)。//此例中,我们先随意附上Id,OrdinaryFileName,Description这三组属性。
NameValueCollection metadata = new NameValueCollection();
metadata[Server.UrlEncode(
"资源编号(新文件名)")] = id;
metadata[Server.UrlEncode(
"原始文件名")] =Server.UrlEncode( fileName);
metadata[Server.UrlEncode(
"描述")] =    Server.UrlEncode(String.IsNullOrEmpty(description) ? "null" : description);
metadata[Server.UrlEncode(
"文件大小")] = size;
metadata[Server.UrlEncode(
"上传时间")] = DateTime.Now.ToString();
properties.Metadata
= metadata;
properties.ContentType
= contentType;
//BlobContents即Blob内容,以byte[]的形式传入
BlobContents fileBlob = new BlobContents(data);
//省去REST的麻烦,直接调用CreateBlob方法。需要传入的参数:Blob属性集、Blob内容、是否覆盖同名
container.CreateBlob(properties, fileBlob, true);
}

  注意:metadata[] 的 name 和 value 均不支持中文。如果需要引入中文的 metadata,我采用的方法是对所有的 name 和 value 进行 UrlEncode,在页面显示的时候再 UrlDecode。

  如:

metadata[Server.UrlEncode( "原始文件名")] =Server.UrlEncode( fileName);

  第六步:

  删除云端文件的方法:

if (this.container.DoesBlobExist(blobName))//如果文件存在
{
this.container.DeleteBlob(blobName); //删除之
}

  同样是使用 BlobContainer  类中的方法,非常容易。

  第七步:

  在 WebRole 项目的 Web.Config 文件中的

  <system.web>节下设置<httpRuntime maxRequestLength="2097151" executionTimeout="3600" />为什么要要这样做?

  2097151B=2MB,即 IIS 能够处理一次请求的最大数据。也就是说,在正常情况下,你是无法直接上传大于 2M 的文件的。

  在 BlobContainer.CreateBlob() 方法中,已经封装了对大文件(大于 2M 的文件)的处理。处理方式即分块方式(Block,详见Azure Services Platform Step by Step-(9) Windows Azure Storage概览。

  好了,到目前为止,我们已经学会:取得 Blob Storage 认证、建立 Container、得到 Container 的实体、在 Container 中存储 Blob、删除 Blob、设置与获取 Blob 的 metadata 属性。

private bool PutLargeBlobImpl(BlobProperties blobProperties, Stream stream, bool overwrite, string eTag)
{
    
bool retval = false;
    
// Since we got a large block, chunk it into smaller pieces called blocks
    long blockSize = BlockSize;
    
long startPosition = stream.Position;
    
long length = stream.Length - startPosition;
    
int numBlocks = (int)Math.Ceiling((double)length / blockSize);
    
string[] blockIds = new string[numBlocks];
    
    
//We can retry only if the stream supports seeking. An alternative is to buffer the data in memory
    
//but we do not do this currently.
    RetryPolicy R = (stream.CanSeek ? this.RetryPolicy : RetryPolicies.NoRetry);
    
    
//Upload each of the blocks, retrying any failed uploads
    for (int i = 0; i < numBlocks; ++i)
    {
        
string blockId = Convert.ToBase64String(System.BitConverter.GetBytes(i));
        blockIds[i]
= blockId;
        R(()
=>
        {
            
// Rewind the stream to appropriate location in case this is a retry
            if (stream.CanSeek)
                stream.Position
= startPosition + i * blockSize;
            NameValueCollection nvc
= new NameValueCollection();
            nvc.Add(QueryParams.QueryParamComp, CompConstants.Block);
            
//The block naming should be more elaborate to give more meanings on GetBlockList
            nvc.Add(QueryParams.QueryParamBlockId, blockId);
            
long blockLength = Math.Min(blockSize, length - stream.Position);
            retval
= UploadData(blobProperties, stream, blockLength, overwrite, eTag, nvc);
        });
    }
    
    
// Now commit the list
    
// First create the output
    using (MemoryStream buffer = new MemoryStream())
    {
        
// construct our own XML segment with correct blockId's
        XmlTextWriter writer = new XmlTextWriter(buffer, Encoding.UTF8);
        writer.WriteStartDocument();
        writer.WriteStartElement(XmlElementNames.BlockList);
        
foreach (string id in blockIds)
        {
            writer.WriteElementString(XmlElementNames.Block, id);
        }
        writer.WriteEndElement();
        writer.WriteEndDocument();
        writer.Flush();
        buffer.Position
= 0; //Rewind
        
        NameValueCollection nvc
= new NameValueCollection();
        nvc.Add(QueryParams.QueryParamComp, CompConstants.BlockList);
        retval
= UploadData(blobProperties, buffer, buffer.Length, overwrite, eTag, nvc);
    }
    
    
return retval;
}
0
相关文章