按照大型網(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)視和編輯: