[學(xué)習(xí)記錄]用Azure Blob作圖片云存儲(chǔ)

按照大型網(wǎng)站架構(gòu)對(duì)CDN(內(nèi)容分發(fā)網(wǎng)絡(luò))的要求,類似于圖片這樣的流量比較大的內(nèi)容就需要放在單獨(dú)的服務(wù)器中訪問,而云計(jì)算晶乔、云存儲(chǔ)能實(shí)現(xiàn)分布式存儲(chǔ)、提高響應(yīng)速度牺勾,以及按需使用正罢、降低使用成本等諸多優(yōu)點(diǎn),已經(jīng)成為互聯(lián)網(wǎng)的基礎(chǔ)服務(wù)與設(shè)施驻民。

現(xiàn)在的云平臺(tái)都要實(shí)名認(rèn)證翻具,我試了下阿里云、又拍云回还、華為云裆泳、騰訊云等,最后發(fā)現(xiàn)還是微軟的Azure比較強(qiáng)大柠硕,它的使用文檔簡潔易讀晾虑,平臺(tái)可操作程度高。它有兩種試用方式:免費(fèi)試用仅叫、1元試用帜篇,申請(qǐng)立即生效,如果不想注冊(cè)诫咱,還可以使用 Azure 存儲(chǔ)模擬器在本地進(jìn)行開發(fā)和測(cè)試笙隙,幾步就搞定,對(duì)學(xué)習(xí)來說很方便坎缭。

我用Visual Studio開發(fā)ASP.Net程序竟痰,這次的學(xué)習(xí)任務(wù)是在前端的UEditor富文本編輯器上傳圖片到單獨(dú)的服務(wù)器并實(shí)時(shí)顯示在編輯框中,這就要用到圖片的云存儲(chǔ)掏呼,Azure官方有個(gè)快速上手的文檔:【通過 .NET 開始使用 Azure Blob 存儲(chǔ)】坏快,我根據(jù)這個(gè)文檔上手,稍加研究以實(shí)現(xiàn)所需要的功能憎夷。

Azure 文件存儲(chǔ)有幾種不同的形式莽鸿,包括:Blob 存儲(chǔ)、文件存儲(chǔ)拾给、表存儲(chǔ)祥得、隊(duì)列存儲(chǔ)。圖片用Blob 存儲(chǔ)蒋得,Blob 是一個(gè)可以存儲(chǔ)二進(jìn)制文件的容器级及,典型的Blob是一張圖片或一個(gè)聲音文件,由于它們的大尺寸额衙,必須使用特殊的方式來處理(例如:上傳饮焦、下載或者存放到一個(gè)數(shù)據(jù)庫)怕吴。

首先得注冊(cè)賬號(hào),創(chuàng)建個(gè)存儲(chǔ)空間县踢。然后用Visual Studio打開解決方案械哟,在項(xiàng)目中添加兩個(gè)NuGet 安裝包:

1.適用于 .NET 的 Microsoft Azure 存儲(chǔ)客戶端庫:在線搜索“WindowsAzure.Storage”,然后單擊“安裝”以安裝存儲(chǔ)客戶端庫和依賴項(xiàng)殿雪。
2.適用于 .NET 的 Microsoft Azure Configuration Manager 庫:在線搜索“ConfigurationManager”暇咆,然后單擊“安裝”以安裝 Azure 配置管理器。```

添加web.config配置(如果是桌面應(yīng)用程序則是app.config)丙曙,在<configuration>的子節(jié)點(diǎn) <appSettings>下添加一個(gè)元素:

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

account-name為存儲(chǔ)帳戶名稱爸业,account-key為存儲(chǔ)帳戶密鑰,這些在Azure門戶管理面板上可以看到亏镰。

封裝一個(gè)類專門用于處理在Azure上的圖片云存儲(chǔ):public class AzureBlob扯旷,需要添加三個(gè)using:

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

修改它的無參構(gòu)造函數(shù),并聲明一個(gè)成員變量 索抓,使得該類每次被創(chuàng)建對(duì)象時(shí)钧忽,創(chuàng)建一個(gè)Blob 服務(wù)客戶端給該變量,方便其他方法調(diào)用:

CloudBlobClient BlobClient;//類的成員逼肯,用于創(chuàng)建Blob 服務(wù)客戶端
public AzureBlob()
{
    //解析配置中的連接字符串
    CloudStorageAccount storageAccount = CloudStorageAccount.Parse(CloudConfigurationManager.GetSetting("StorageConnectionString"));
    //創(chuàng)建 Blob 服務(wù)客戶端
    BlobClient = storageAccount.CreateCloudBlobClient();
}

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

它們有一些命名規(guī)則耸黑,其中Blob命名可以無擴(kuò)展名,也可以加擴(kuò)展名表示不同格式的文件,如“***.jpg篮幢、***.txt”大刊,blob名稱中還可以包含路徑信息,如2016/11/photo1.jpg三椿,這將創(chuàng)建一個(gè)虛擬目錄結(jié)構(gòu)缺菌,可以像傳統(tǒng)文件系統(tǒng)一樣組織和遍歷。所以創(chuàng)建了服務(wù)客戶端只是相當(dāng)于與云建立連接搜锰,還需要添加對(duì)容器和Blob的引用伴郁,將它們封裝進(jìn)一個(gè)方法中:

/// <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;
}

需要提到的一點(diǎn)就是Azure 存儲(chǔ)空間提供三種類型的 Blob:塊 Blob、頁 Blob 和追加 Blob蛋叼。塊 Blob特別適用于存儲(chǔ)短的文本或二進(jìn)制文件焊傅,例如文檔和媒體文件。追加 Blob 類似于塊 Blob鸦列,因?yàn)樗鼈兪怯蓧K組成的租冠,但針對(duì)追加操作對(duì)它們進(jìn)行了優(yōu)化,因此它們適用于日志記錄方案薯嗤。頁 Blob 最大可達(dá) 1 TB 大小,并且對(duì)于頻繁的讀/寫操作更加高效纤泵,Azure 中的虛擬機(jī)就使用頁 Blob 作為 OS 和數(shù)據(jù)磁盤骆姐。圖片塊 Blob類型CloudBlockBlob镜粤。

然后寫上傳圖片的方法,因?yàn)閺木庉嬈鞯玫降膱D片經(jīng)控制器處理轉(zhuǎn)換成了byte[] 數(shù)組的形式玻褪,所以這里以UploadFromByteArrayAsync的方式上傳肉渴,Azure Blob還支持Stream文件流、文件路徑等上傳方式带射。Azure支持異步操作同规,UploadFromByteArrayAsync返回的Task類對(duì)象就是異步操作,可以拿它查看錯(cuò)誤信息:

/// <param name="bytes">二進(jìn)制形式的文件</param>
/// <returns>異步信息</returns>
public Task UploadToBlob(string fileName, string mycontainer, byte[] bytes)
{
    CloudBlobContainer container = BlobClient.GetContainerReference(mycontainer);//獲取容器的引用
    //創(chuàng)建一個(gè)容器(如果該容器不存在)
    container.CreateIfNotExists();
    //設(shè)置該容器為公共容器窟社,也就是說網(wǎng)絡(luò)上能訪問容器中的文件券勺,但不能修改、刪除
    container.SetPermissions(new BlobContainerPermissions { PublicAccess = BlobContainerPublicAccessType.Blob });
    //將Blob(文件)上載到容器中灿里,如果已存在同名Blob关炼,則覆蓋它
    CloudBlockBlob blockBlob = container.GetBlockBlobReference(fileName);//獲取塊 Blob 引用
    Task result= blockBlob.UploadFromByteArrayAsync(bytes, 0, bytes.Length);//將二進(jìn)制文件上傳
    return result;
}

上一步可以直接拿到blob的url地址,但有時(shí)候并不一定要上傳匣吊,為了方便根據(jù)文件名和容器名取到Blob地址儒拂,也寫一個(gè)方法:

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

容器和Blob其實(shí)就相當(dāng)于文件夾、文件名色鸳,它們和存儲(chǔ)器地址三者合起來就是圖片上傳后的路徑社痛,比如
存儲(chǔ)器地址:https://abcd.blob.core.chinacloudapi.cn
容器名:ueditor
Blob名:12/29/8ace5d.png
那么上傳后的地址是:https://abcd.blob.core.chinacloudapi.cn/ueditor/12/29/8ace5d.png
這個(gè)地址可以直接在瀏覽器上訪問(前提是Blob容器的訪問權(quán)限已設(shè)置為公共),瀏覽器默認(rèn)為行為是下載命雀。將地址寫入html標(biāo)簽褥影,由客戶端解析就可以在頁面上顯示。
再封裝下載和刪除的方法:

/// <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();
}

至此這個(gè)類封裝完成了咏雌,它可以實(shí)現(xiàn)簡單的圖片上傳凡怎、下載、查詢和刪除功能赊抖。
然后在后端處理前端請(qǐng)求的控制程序上調(diào)用它统倒,我用UEditor這個(gè)編輯器,找到 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值+文件擴(kuò)展名房匆,
string upFileName = CommonHelper.CalcMD5(uploadFileBytes) + Path.GetExtension(uploadFileName);
//文件上傳的虛擬路徑(作為Blob名)="當(dāng)前年份/當(dāng)前月份/當(dāng)前日期/文件名"
DateTime today = DateTime.Today;
string unVirtualPath = today.Year + "/" + today.Month + "/" + today.Day + "/" + upFileName;
ILog logger = LogManager.GetLogger(typeof(UploadHandler));//用log4net記錄系統(tǒng)日志
try {//上傳到Azure云存儲(chǔ)  
    AzureBlob azureBlob = new AzureBlob();
    Task uploadResult = azureBlob.UploadToBlob(unVirtualPath, "ueditor", uploadFileBytes);
    while (!uploadResult.IsCompleted)//IsCompleted:判斷上傳是否完成
    {//while 等待上傳完成
        if (uploadResult.Exception != null)
        {//Exception:如果發(fā)生異常則返回異常信息,如果上傳完成且未發(fā)生異常則返回null
            Result.State = UploadState.FileAccessError;//響應(yīng)編輯器表示上傳失敗
            Result.ErrorMessage = "上傳文件失敗";//提示給編輯器的錯(cuò)誤信息
            logger.Error("上傳失敗,錯(cuò)誤信息:" + uploadResult.Exception.GetBaseException());//日志中記錄錯(cuò)誤
            return;
        }
    }
    Result.Url = azureBlob.GetBlobURI(unVirtualPath, "ueditor");//將上傳成功的文件地址返回給UEditor前端
    Result.State = UploadState.Success;//響應(yīng)編輯器表示上傳成功
}catch (Exception ex){
    Result.State = UploadState.FileAccessError;
    Result.ErrorMessage = ex.Message;
    logger.Error("上傳文件失敗报亩,發(fā)生異常:", ex);
}finally{
    WriteResult();//將客戶端與編輯器有關(guān)的內(nèi)容響應(yīng)給客戶端
}

UEditor前端上傳的時(shí)候默認(rèn)會(huì)在文件地址上加個(gè)前綴浴鸿,這里不需要了,找到配置文件config.json弦追,修改其中的imageUrlPrefix項(xiàng)為:"imageUrlPrefix": "", /* 圖片訪問路徑前綴 */岳链。
測(cè)試結(jié)果:

成功了!可以在Azure門戶面板上監(jiān)視和編輯:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末劲件,一起剝皮案震驚了整個(gè)濱河市掸哑,隨后出現(xiàn)的幾起案子约急,更是在濱河造成了極大的恐慌,老刑警劉巖苗分,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件厌蔽,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡摔癣,警方通過查閱死者的電腦和手機(jī)奴饮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來择浊,“玉大人戴卜,你說我怎么就攤上這事〗” “怎么了叉瘩?”我有些...
    開封第一講書人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長粘捎。 經(jīng)常有香客問我薇缅,道長,這世上最難降的妖魔是什么攒磨? 我笑而不...
    開封第一講書人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任泳桦,我火速辦了婚禮,結(jié)果婚禮上娩缰,老公的妹妹穿的比我還像新娘灸撰。我一直安慰自己,他們只是感情好拼坎,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開白布浮毯。 她就那樣靜靜地躺著,像睡著了一般泰鸡。 火紅的嫁衣襯著肌膚如雪债蓝。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評(píng)論 1 305
  • 那天盛龄,我揣著相機(jī)與錄音饰迹,去河邊找鬼。 笑死余舶,一個(gè)胖子當(dāng)著我的面吹牛啊鸭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播匿值,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼赠制,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了千扔?” 一聲冷哼從身側(cè)響起憎妙,我...
    開封第一講書人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤库正,失蹤者是張志新(化名)和其女友劉穎曲楚,沒想到半個(gè)月后厘唾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡龙誊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年抚垃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片趟大。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡鹤树,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出逊朽,到底是詐尸還是另有隱情罕伯,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布叽讳,位于F島的核電站追他,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏岛蚤。R本人自食惡果不足惜邑狸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望涤妒。 院中可真熱鬧单雾,春花似錦、人聲如沸她紫。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贿讹。三九已至渐逃,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間围详,已是汗流浹背朴乖。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留助赞,地道東北人买羞。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像雹食,于是被迫代替她去往敵國和親畜普。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容