美文网首页程序员今日看点
[学习记录]用Azure Blob作图片云存储

[学习记录]用Azure Blob作图片云存储

作者: 行不易一 | 来源:发表于2016-11-17 06:58 被阅读2261次

    按照大型网站架构对CDN(内容分发网络)的要求,类似于图片这样的流量比较大的内容就需要放在单独的服务器中访问,而云计算、云存储能实现分布式存储、提高响应速度,以及按需使用、降低使用成本等诸多优点,已经成为互联网的基础服务与设施。

    现在的云平台都要实名认证,我试了下阿里云、又拍云、华为云、腾讯云等,最后发现还是微软的Azure比较强大,它的使用文档简洁易读,平台可操作程度高。它有两种试用方式:免费试用、1元试用,申请立即生效,如果不想注册,还可以使用 Azure 存储模拟器在本地进行开发和测试,几步就搞定,对学习来说很方便。

    我用Visual Studio开发ASP.Net程序,这次的学习任务是在前端的UEditor富文本编辑器上传图片到单独的服务器并实时显示在编辑框中,这就要用到图片的云存储,Azure官方有个快速上手的文档:【通过 .NET 开始使用 Azure Blob 存储】,我根据这个文档上手,稍加研究以实现所需要的功能。

    Azure 文件存储有几种不同的形式,包括:Blob 存储、文件存储、表存储、队列存储。图片用Blob 存储,Blob 是一个可以存储二进制文件的容器,典型的Blob是一张图片或一个声音文件,由于它们的大尺寸,必须使用特殊的方式来处理(例如:上传、下载或者存放到一个数据库)。

    首先得注册账号,创建个存储空间。然后用Visual Studio打开解决方案,在项目中添加两个NuGet 安装包:

    1.适用于 .NET 的 Microsoft Azure 存储客户端库:在线搜索“WindowsAzure.Storage”,然后单击“安装”以安装存储客户端库和依赖项。
    2.适用于 .NET 的 Microsoft Azure Configuration Manager 库:在线搜索“ConfigurationManager”,然后单击“安装”以安装 Azure 配置管理器。```
    
    添加web.config配置(如果是桌面应用程序则是app.config),在<configuration>的子节点 <appSettings>下添加一个元素:
    

    <add key="StorageConnectionString" value="DefaultEndpointsProtocol=https;AccountName=account-name;AccountKey=account-key;EndpointSuffix=core.chinacloudapi.cn" />```

    account-name为存储帐户名称,account-key为存储帐户密钥,这些在Azure门户管理面板上可以看到。

    封装一个类专门用于处理在Azure上的图片云存储:public class AzureBlob,需要添加三个using:

    using Microsoft.Azure; 
    using Microsoft.WindowsAzure.Storage; 
    using Microsoft.WindowsAzure.Storage.Blob; 
    

    修改它的无参构造函数,并声明一个成员变量 ,使得该类每次被创建对象时,创建一个Blob 服务客户端给该变量,方便其他方法调用:

    CloudBlobClient BlobClient;//类的成员,用于创建Blob 服务客户端
    public AzureBlob()
    {
        //解析配置中的连接字符串
        CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));
        //创建 Blob 服务客户端
        BlobClient = storageAccount.CreateCloudBlobClient();
    }
    

    Azure提供的Blob包含以下组件:

    它们有一些命名规则,其中Blob命名可以无扩展名,也可以加扩展名表示不同格式的文件,如“***.jpg***.txt”,blob名称中还可以包含路径信息,如2016/11/photo1.jpg,这将创建一个虚拟目录结构,可以像传统文件系统一样组织和遍历。所以创建了服务客户端只是相当于与云建立连接,还需要添加对容器和Blob的引用,将它们封装进一个方法中:

    /// <param name="mycontainer">容器名</param>
    /// <param name="fileName">文件名</param>
    public CloudBlockBlob GetContainer(string mycontainer,string fileName)
    {
         //获取容器的引用
         CloudBlobContainer container = BlobClient.GetContainerReference(mycontainer);
         //获取块 Blob 引用
         CloudBlockBlob blob = container.GetBlockBlobReference(fileName);
         return blob;
    }
    

    需要提到的一点就是Azure 存储空间提供三种类型的 Blob:块 Blob、页 Blob 和追加 Blob。块 Blob特别适用于存储短的文本或二进制文件,例如文档和媒体文件。追加 Blob 类似于块 Blob,因为它们是由块组成的,但针对追加操作对它们进行了优化,因此它们适用于日志记录方案。页 Blob 最大可达 1 TB 大小,并且对于频繁的读/写操作更加高效,Azure 中的虚拟机就使用页 Blob 作为 OS 和数据磁盘。图片块 Blob类型CloudBlockBlob

    然后写上传图片的方法,因为从编辑器得到的图片经控制器处理转换成了byte[] 数组的形式,所以这里以UploadFromByteArrayAsync的方式上传,Azure Blob还支持Stream文件流、文件路径等上传方式。Azure支持异步操作,UploadFromByteArrayAsync返回的Task类对象就是异步操作,可以拿它查看错误信息:

    /// <param name="bytes">二进制形式的文件</param>
    /// <returns>异步信息</returns>
    public Task UploadToBlob(string fileName, string mycontainer, byte[] bytes)
    {
        CloudBlobContainer container = BlobClient.GetContainerReference(mycontainer);//获取容器的引用
        //创建一个容器(如果该容器不存在)
        container.CreateIfNotExists();
        //设置该容器为公共容器,也就是说网络上能访问容器中的文件,但不能修改、删除
        container.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Blob });
        //将Blob(文件)上载到容器中,如果已存在同名Blob,则覆盖它
        CloudBlockBlob blockBlob = container.GetBlockBlobReference(fileName);//获取块 Blob 引用
        Task result= blockBlob.UploadFromByteArrayAsync(bytes, 0, bytes.Length);//将二进制文件上传
        return result;
    }
    

    上一步可以直接拿到blob的url地址,但有时候并不一定要上传,为了方便根据文件名和容器名取到Blob地址,也写一个方法:

    public string GetBlobURI(string fileName, string mycontainer)
    {
        CloudBlockBlob blob = GetContainer(mycontainer,fileName);
        return blob.Uri.ToString();
    }
    

    容器和Blob其实就相当于文件夹、文件名,它们和存储器地址三者合起来就是图片上传后的路径,比如
    存储器地址:https://abcd.blob.core.chinacloudapi.cn
    容器名:ueditor
    Blob名:12/29/8ace5d.png
    那么上传后的地址是:https://abcd.blob.core.chinacloudapi.cn/ueditor/12/29/8ace5d.png
    这个地址可以直接在浏览器上访问(前提是Blob容器的访问权限已设置为公共),浏览器默认为行为是下载。将地址写入html标签,由客户端解析就可以在页面上显示。
    再封装下载和删除的方法:

    /// <summary>
    /// 下载Blob
    /// </summary>
    public void DownloadToFile(string fileName, string mycontainer, string fliePath)
    {
        CloudBlockBlob blob = GetContainer(mycontainer, fileName);
        using (var fileStream = File.OpenWrite(fliePath))
        {
            blob.DownloadToStream(fileStream); //将blob保存在指定路径
        }
    }
    /// <summary>
    /// 删除Blob
    /// </summary>
    public void DeleteBlob (string fileName, string mycontainer)
    {
        CloudBlockBlob blob = GetContainer(mycontainer, fileName);
        blob.Delete();
    }
    

    至此这个类封装完成了,它可以实现简单的图片上传、下载、查询和删除功能。
    然后在后端处理前端请求的控制程序上调用它,我用UEditor这个编辑器,找到 UploadHandler.cs,修改下面的一段代码:

    /*
    var savePath = PathFormatter.Format(uploadFileName, UploadConfig.PathFormat);
    var localPath = Server.MapPath(savePath);
    try{
        if (!Directory.Exists(Path.GetDirectoryName(localPath))){
            Directory.CreateDirectory(Path.GetDirectoryName(localPath));
        }
        File.WriteAllBytes(localPath, uploadFileBytes);
        Result.Url = savePath;
        Result.State = UploadState.Success;
    }catch (Exception e){
        Result.State = UploadState.FileAccessError;
        Result.ErrorMessage = e.Message;
    }finally{
        WriteResult();
    }
    

    改成这样:

    //文件名=文件的MD5值+文件扩展名,
    string upFileName = CommonHelper.CalcMD5(uploadFileBytes) + Path.GetExtension(uploadFileName);
    //文件上传的虚拟路径(作为Blob名)="当前年份/当前月份/当前日期/文件名"
    DateTime today = DateTime.Today;
    string unVirtualPath = today.Year + "/" + today.Month + "/" + today.Day + "/" + upFileName;
    ILog logger = LogManager.GetLogger(typeof(UploadHandler));//用log4net记录系统日志
    try {//上传到Azure云存储  
        AzureBlob azureBlob = new AzureBlob();
        Task uploadResult = azureBlob.UploadToBlob(unVirtualPath, "ueditor", uploadFileBytes);
        while (!uploadResult.IsCompleted)//IsCompleted:判断上传是否完成
        {//while 等待上传完成
            if (uploadResult.Exception != null)
            {//Exception:如果发生异常则返回异常信息,如果上传完成且未发生异常则返回null
                Result.State = UploadState.FileAccessError;//响应编辑器表示上传失败
                Result.ErrorMessage = "上传文件失败";//提示给编辑器的错误信息
                logger.Error("上传失败,错误信息:" + uploadResult.Exception.GetBaseException());//日志中记录错误
                return;
            }
        }
        Result.Url = azureBlob.GetBlobURI(unVirtualPath, "ueditor");//将上传成功的文件地址返回给UEditor前端
        Result.State = UploadState.Success;//响应编辑器表示上传成功
    }catch (Exception ex){
        Result.State = UploadState.FileAccessError;
        Result.ErrorMessage = ex.Message;
        logger.Error("上传文件失败,发生异常:", ex);
    }finally{
        WriteResult();//将客户端与编辑器有关的内容响应给客户端
    }
    

    UEditor前端上传的时候默认会在文件地址上加个前缀,这里不需要了,找到配置文件config.json,修改其中的imageUrlPrefix项为:"imageUrlPrefix": "", /* 图片访问路径前缀 */
    测试结果:

    成功了!可以在Azure门户面板上监视和编辑:

    相关文章

      网友评论

        本文标题:[学习记录]用Azure Blob作图片云存储

        本文链接:https://www.haomeiwen.com/subject/nucdpttx.html