云计算 频道

使用Blob Storage搭建简单网络硬盘

  【IT168 资讯】现在我们示范一下,如何用最简单的代码,将Blob Storage带入我们的生活。

  虽然Windows Azure Storage都提供了REST的编程接口,但大家在实际编程中,根本不愿意去和繁琐的HttpWebRequest打交道吧?微软在这方面做得非常体贴,它将所有Windows Azure Storage的REST编程接口都封装到了C#类中。我们在实际开发时,只需要引用该Project或者它编译成的dll,就可以告别裸露的REST,使用那些非常方便的类和方法了!

  该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属性。

  是不是很容易呢?

  【寻根问底】StorageClient项目中Block处理过程的代码

 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);
                    nvc.Add(QueryParams.QueryParamBlockId, blockId); // The block naming should be more elaborate to give more meanings on GetBlockList
                    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
相关文章