項目須要將音視頻文件上傳服務器,考慮併發要求高,經過七牛來實現。html
作了一個簡易的壓力測試,同時上傳多個文件,七牛本身應該有隊列處理併發請求,我不管同時提交多少個文件,七牛是批量一個個排隊處理了。java
一個1.5MB的文件,上傳時間大概2-3秒,感受不錯。 算法
直接上代碼api
using Qiniu.IO; using Qiniu.IO.Resumable; using Qiniu.RPC; using Qiniu.RS; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; namespace qiniuTest { /// <summary> /// 文件上傳有兩種方式: /// 一種是以普通方式直傳文件,簡稱普通上傳; /// 另外一種方式是斷點續上傳,斷點續上傳在網絡條件很通常的狀況下也能有出色的上傳速度,並且對大文件的傳輸很是友好。 /// </summary> class Program { static string bucket = "cvteXXXX"; static void Main(string[] args) { Qiniu.Conf.Config.ACCESS_KEY = "6QQ7Cnz4bljdkQOWQ5UOAheVCAd0bCa7Tc5XXXXX"; Qiniu.Conf.Config.SECRET_KEY = "9rUGnbFtvm-PLWcZeOR6ed9MUjZ4bKitf7YXXXX"; string fileKey = "應用系統全貌圖.png"; //GetFileStat(bucket, fileKey); //小文件直傳 string fileName = "CVTE信息系統-業務功能架構圖-IM和企業微信.jpg"; //PutFile(bucket, Guid.NewGuid().ToString() + fileName, "d:\\" + fileName); //在asp.net mvc中的文件上傳 //ResumablePutFile(bucket, Guid.NewGuid().ToString(), Path.Combine(path, Request.Form[0])); //大文件上傳 //string bigFileName = "eclipse-java-luna-SR1-win32-x86_64.zip"; //ResumablePutFile(bucket, Guid.NewGuid().ToString() + bigFileName, "d:\\Software\\" + bigFileName); //GetFile("7xq1c1.com1.z0.glb.clouddn.com", fileKey); //********************** 壓力測試 ********************** // 獲取線程池的最大線程數和維護的最小空閒線程數 int maxThreadNum, portThreadNum; int minThreadNum; ThreadPool.GetMaxThreads(out maxThreadNum, out portThreadNum); ThreadPool.GetMinThreads(out minThreadNum, out portThreadNum); Console.WriteLine("最大線程數:{0}", maxThreadNum); Console.WriteLine("最小空閒線程數:{0}", minThreadNum); int loopNumber = 1; //內部循環次數 int ConcurrentNumber = 10; //併發數 for (int i = 0; i < ConcurrentNumber; i++) { ThreadPool.QueueUserWorkItem(new WaitCallback(TaskProc), loopNumber); } Console.ReadLine(); } public static void TaskProc(object loopNumber) { int LoopNumber = Convert.ToInt32(loopNumber); Console.WriteLine("啓動任務,小文件直傳"); //小文件直傳 壓力測試 for (int i = 0; i < LoopNumber; i++) { string fileName = "WinRAR.exe"; Console.WriteLine(i + "開始" + fileName + System.DateTime.Now); PutFile(bucket, Guid.NewGuid().ToString() + fileName, "D:\\" + fileName); Console.WriteLine(i + "完成" + fileName + System.DateTime.Now); string fileName1 = "WinRAR1.exe"; Console.WriteLine(i + "開始" + fileName1 + System.DateTime.Now); PutFile(bucket, Guid.NewGuid().ToString() + fileName1, "D:\\" + fileName1); Console.WriteLine(i + "完成" + fileName1 + System.DateTime.Now); } } /// <summary> /// 查看單個文件屬性信息 /// </summary> /// <param name="bucket">七牛雲存儲空間名</param> /// <param name="key">文件key,也就是文件名</param> public static void GetFileStat(string bucket, string key) { RSClient client = new RSClient(); Entry entry = client.Stat(new EntryPath(bucket, key)); if (entry.OK) { Console.WriteLine("Hash: " + entry.Hash); Console.WriteLine("Fsize: " + entry.Fsize); Console.WriteLine("PutTime: " + entry.PutTime); Console.WriteLine("MimeType: " + entry.MimeType); Console.WriteLine("Customer: " + entry.Customer); } else { Console.WriteLine("Failed to Stat"); } } /// <summary> /// 刪除單個文件 /// </summary> /// <param name="bucket">文件所在的空間名</param> /// <param name="key">文件key</param> public static void Delete(string bucket, string key) { Console.WriteLine("\n===> Delete {0}:{1}", bucket, key); RSClient client = new RSClient(); CallRet ret = client.Delete(new EntryPath(bucket, key)); if (ret.OK) { Console.WriteLine("Delete OK"); } else { Console.WriteLine("Failed to delete"); } } /// <summary> /// 批量刪除文件 /// </summary> /// <param name="bucket">文件所在的空間名</param> /// <param name="keys">文件key</param> public static void BatchDelete(string bucket, string[] keys) { RSClient client = new RSClient(); List<EntryPath> EntryPaths = new List<EntryPath>(); foreach (string key in keys) { Console.WriteLine("\n===> Stat {0}:{1}", bucket, key); EntryPaths.Add(new EntryPath(bucket, key)); } client.BatchDelete(EntryPaths.ToArray()); } /// <summary> /// 普通方式直傳文件 /// </summary> /// <param name="bucket">文件所在的空間名</param> /// <param name="key">您能夠自行定義文件Key,通常GUID</param> /// <param name="fname">文件路徑+文件名</param> public static void PutFile(string bucket, string key, string fname) { var policy = new PutPolicy(bucket, 3600); string upToken = policy.Token(); PutExtra extra = new PutExtra(); IOClient client = new IOClient(); client.PutFile(upToken, key, fname, extra); } /// <summary> /// 斷點續上傳方式,傳大文件用這種方式 /// </summary> /// <param name="bucket">文件所在的空間名</param> /// <param name="key">您能夠自行定義文件Key,通常GUID</param> /// <param name="fname">文件路徑+文件名</param> public static void ResumablePutFile(string bucket, string key, string fname) { Console.WriteLine("\n===> ResumablePutFile {0}:{1} fname:{2}", bucket, key, fname); PutPolicy policy = new PutPolicy(bucket, 3600); string upToken = policy.Token(); Settings setting = new Settings(); ResumablePutExtra extra = new ResumablePutExtra(); ResumablePut client = new ResumablePut(setting, extra); client.PutFile(upToken, fname, Guid.NewGuid().ToString()); } /// <summary> /// Get方式獲取文件 /// </summary> /// <param name="domain">文件域</param> /// <param name="key">文件Key</param> public static void GetFile(string domain, string key) { System.Diagnostics.Process.Start("http://" + domain + "/" + key); } } }
另外,七牛的魔法變量很是強大,多用於加強回調七牛雲存儲
魔法變量是一組預先定義的變量,可使用 $(var)
或 $(var.field_name)
形式求值。服務器
目前可用的魔法變量以下:微信
變量名 | 包含子項 | 變量說明 | 適用範圍 |
bucket | 得到上傳的目標空間名。 | ||
key | 得到文件保存在空間中的資源名。 | ||
etag | 文件上傳成功後的HTTP ETag。若上傳時未指定資源ID,Etag將做爲資源ID使用。 | ||
fname | 上傳的原始文件名。 | 不支持用於分片上傳 |
|
fsize | 資源尺寸,單位爲字節。 | ||
mimeType | 資源類型,好比JPG圖片的資源類型爲image/jpg 。 |
||
endUser | 上傳時指定的endUser 字段,一般用於區分不一樣終端用戶的請求。 |
||
persistentId | 音視頻轉碼持久化的進度查詢ID。 | ||
exif | 是 | 獲取所上傳圖片的Exif信息。 該變量包含子字段,好比對 |
暫不支持用於saveKey 中 |
imageInfo | 是 | 獲取所上傳圖片的基本信息。 該變量包含子字段,好比對 |
暫不支持用於saveKey 中 |
year | 上傳時的年份。 | 暫不支持用於’returnBody’、’callbackBody’中 | |
mon | 上傳時的月份。 | 暫不支持用於’returnBody’、’callbackBody’中 | |
day | 上傳時的日期。 | 暫不支持用於’returnBody’、’callbackBody’中 | |
hour | 上傳時的小時。 | 暫不支持用於’returnBody’、’callbackBody’中 | |
min | 上傳時的分鐘。 | 暫不支持用於’returnBody’、’callbackBody’中 | |
sec | 上傳時的秒鐘。 | 暫不支持用於’returnBody’、’callbackBody’中 | |
avinfo | 是 | 音視頻資源的元信息。 | 暫不支持用於’saveKey’中 |
imageAve | 圖片主色調,算法由Camera360友情提供。 | ||
ext | 上傳資源的後綴名,經過自動檢測的 mimeType 或者原文件的後綴來獲取。 | 不支持用於分片上傳 |
|
uuid | 生成uuid | 暫不支持用於’saveKey’中 | |
bodySha1 | callbackBody的sha1(hex編碼) | 只支持用於’callbackUrl’中 |
魔法變量支持$(<Object>.<Property>)
形式的訪問子項,例如:併發
求值舉例:
$(bucket)
- 得到上傳目標bucket名字$(imageInfo)
- 獲取當前上傳圖片的基本屬性信息$(imageInfo.height)
- 獲取當前上傳圖片的原始高度能夠設置上傳策略(PutPolicy)中的callbackUrl
字段,而且設置callbackBody
字段。
「自定義回調」具體實現
/// <summary> /// 文件上傳後的自定義回調 /// </summary> /// <param name="bucket">文件所在的空間名</param> /// <param name="key">您能夠自行定義文件Key,通常GUID</param> /// <param name="fname">文件路徑+文件名</param> public static PutRet PutFile(string bucket, string key, string fname) { var policy = new PutPolicy(bucket, 3600); policy.ReturnBody = "{\"key\": $(key), \"hash\": $(etag), \"extra\": $(x:extra), \"callbackUrl\": $(x:callbackUrl)}"; //policy.CallBackBody = "name=$(fname)&location=$(x:location)&price=$(x:price)"; //policy.CallBackUrl = "http://ip/url"; string upToken = policy.Token(); PutExtra extra = new PutExtra(); //擴展屬性 Dictionary<string,string> dict =new Dictionary<string,string>(); dict.Add("x:extra", "location=shanghai&age=28"); dict.Add("x:callbackUrl", "http://127.0.0.1/callback"); extra.Params = dict; IOClient client = new IOClient(); return client.PutFile(upToken, key, fname, extra); }
完整源代碼下載 :source code