项目需要将音视频文件上传服务器,考虑并发要求高,通过七牛来实现。

做了一个简易的压力测试,同时上传多个文件,七牛自己应该有队列处理并发请求,我无论同时提交多少个文件,七牛是批量一个个排队处理了。

一个1.5MB的文件,上传时间大概2-3秒,感觉不错。 

 

直接上代码

 

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信息。

该变量包含子字段,比如对$(exif.ApertureValue.val)取值将得到该图片拍摄时的光圈值。

暂不支持用于saveKey
imageInfo获取所上传图片的基本信息。

该变量包含子字段,比如对$(imageInfo.width)取值将得到该图片的宽度。

暂不支持用于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>)形式的访问子项,例如:

  • $(<var>)
  • $(<var>.<field_name>)
  • $(<var>.<field_name>.<sub_field_name>)

求值举例:

  • $(bucket) - 获得上传目标bucket名字
  • $(imageInfo) - 获取当前上传图片的基本属性信息
  • $(imageInfo.height) - 获取当前上传图片的原始高度

回调通知(callback)

可以设置上传策略(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

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐