diff --git a/.gitignore b/.gitignore index 9082a585..596c2d03 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,8 @@ *.swp *.suo +test-results +csharp-sdk.userprefs + bin -obj \ No newline at end of file +obj diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 00000000..c168f9cb --- /dev/null +++ b/.travis.yml @@ -0,0 +1,15 @@ +language: c + +# Make sure build dependencies are installed. +install: + - sudo apt-get update && sudo apt-get install nunit mono-gmcs cli-common-dev libgl1-mesa-dev libsdl1.2-dev libopenal-dev + +before_script: + - export QINIU_ACCESS_KEY="IFkgYHCdmdNPWNjMMhk9LAV7guz1wpI5Sp5h8ssK" + - export QINIU_SECRET_KEY="R6Nlsu6SQwStqhlDv-e4fMGW3qM4ryBspHgdjAFR" + - export QINIU_TEST_BUCKET="icattlecoder3" + - export QINIU_TEST_DOMAIN="qiniuphotos.qiniudn.com" + +script: + - make + - make test diff --git a/CHANGELOG.md b/CHANGELOG.md index 2869bbf2..b58110f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,15 @@ ## CHANGE LOG -### v2.4.0 - -2013-02-19 - -Issue [#10](https://github.com/qiniu/csharp-sdk/pull/10): - -- QBox.Auth.AuthPolicy 增加成员:CallbackBodyType, Escape, AsyncOps, ReturnBody -- DownloadToken支持:增加 QBox.Auth.DownloadPolicy 类 -- 增加 PutAuth 支持:增加 QBox.Auth.PutAuthClient 类 -- 非兼容调整:QBox.RS.Client 改名为 QBox.RPC.Client -- 简易断点续上传支持: 增加 QBox.RS.ResumablePut 类 -- hotfix: 修复了 Base64 编码不支持中文的情况(QBox/Util) - +### v6.0.0 + +2013-07-30 #issue[17](https://github.com/qiniu/csharp-sdk/pull/17) + +- 开发环境支持Xamarin,Mono +- 增加tools,包含Json库,nunit.framework.dll +- 移植单元测试,由原VisulStudio支持变换为Nunit Framework支持 +- QBox命名空间更新为Qiniu +- 遵循 [Qiniu API SDKSpec 6.0](https://github.com/qiniu/sdkspec/tree/v6.0.0) +- 增加 RSF 支持:增加 Qiniu.RSF.RSFClient 类 +- 增加 RS批量操作 +- 引用第三方JSON库,[JSON.NET](json.codeplex.com) +- 大文件断点并行上传支持: 增加 Qiniu.IO.Resumable.ResumablePut 类 diff --git a/Demo/Demo.cs b/Demo/Demo.cs deleted file mode 100644 index 3513a186..00000000 --- a/Demo/Demo.cs +++ /dev/null @@ -1,300 +0,0 @@ -using System; -using QBox.Auth; -using QBox.RS; -using QBox.FileOp; -using QBox.RPC; -using QBox.Util; - -namespace QBox.Demo -{ - public class Demo - { - public static string bucketName; - public static string key; - public static string localFile; - public static string bigkey; - public static string bigFile; - public static string DEMO_DOMAIN; - public static Client conn; - public static RSService rs; - - public static void Main() - { - Config.ACCESS_KEY = ""; - Config.SECRET_KEY = ""; - - bucketName = "yourbucket"; - DEMO_DOMAIN = bucketName + ".qiniudn.com"; - key = "gogopher.jpg"; - localFile = "Resource/gogopher.jpg"; - bigkey = key; - bigFile = localFile; - - conn = new DigestAuthClient(); - rs = new RSService(conn, bucketName); - - MkBucket(); - RSClientPutFile(); - RSClientPutFileWithCRC32(); - Get(key); - ResumablePutFile(); - Stat(bigkey); - Delete(key); - Drop(); - - MkBucket(); - ImageOps(); - - MakeDownloadToken(); - - Console.ReadLine(); - } - - public static void MkBucket() - { - Console.WriteLine("\n===> RSService.MkBucket"); - CallRet callRet = rs.MkBucket(); - PrintRet(callRet); - } - - public static void RSClientPutFile() - { - Console.WriteLine("\n===> RSClient Generate UpToken"); - var authPolicy = new AuthPolicy(bucketName, 3600); - string upToken = authPolicy.MakeAuthTokenString(); - Console.WriteLine("upToken: " + upToken); - - Console.WriteLine("\n===> RSClient.PutFileWithUpToken"); - PutFileRet putFileRet = RSClient.PutFileWithUpToken(upToken, bucketName, key, null, localFile, null, "key="); - PrintRet(putFileRet); - if (putFileRet.OK) - { - Console.WriteLine("Hash: " + putFileRet.Hash); - } - else - { - Console.WriteLine("Failed to RSClient.PutFileWithUpToken"); - } - } - - public static void RSClientPutFileWithCRC32() - { - Console.WriteLine("\n===> RSClientPutFileWithCRC32 Generate CRC32"); - UInt32 crc = CRC32.CheckSumFile(localFile); - Console.WriteLine("CRC32: " + crc.ToString()); - - Console.WriteLine("\n===> RSClientPutFileWithCRC32 Generate UpToken"); - var authPolicy = new AuthPolicy(bucketName, 3600); - string upToken = authPolicy.MakeAuthTokenString(); - Console.WriteLine("upToken: " + upToken); - - Console.WriteLine("\n===> RSClient.PutFileWithUpToken(CRC32)"); - PutFileRet putFileRet = RSClient.PutFileWithUpToken(upToken, bucketName, key, null, localFile, null, "key=", crc); - PrintRet(putFileRet); - if (putFileRet.OK) - { - Console.WriteLine("Hash: " + putFileRet.Hash); - } - else - { - Console.WriteLine("Failed to RSClient.PutFileWithUpToken(CRC32)"); - } - } - - public static void ResumablePutFile() - { - Console.WriteLine("\n===> ResumablePut.PutFile"); - var authPolicy = new AuthPolicy(bucketName, 3600); - string upToken = authPolicy.MakeAuthTokenString(); - PutAuthClient client = new PutAuthClient(upToken); - PutFileRet putFileRet = ResumablePut.PutFile(client, bucketName, bigkey, null, bigFile, null, "key="); - PrintRet(putFileRet); - if (putFileRet.OK) - { - Console.WriteLine("Hash: " + putFileRet.Hash); - } - else - { - Console.WriteLine("Failed to ResumablePut.PutFile"); - } - } - - public static void Get(string key) - { - Console.WriteLine("\n===> RSService.Get"); - GetRet getRet = rs.Get(key, "attName"); - PrintRet(getRet); - if (getRet.OK) - { - Console.WriteLine("Hash: " + getRet.Hash); - Console.WriteLine("FileSize: " + getRet.FileSize); - Console.WriteLine("MimeType: " + getRet.MimeType); - Console.WriteLine("Url: " + getRet.Url); - } - else - { - Console.WriteLine("Failed to Get"); - } - - Console.WriteLine("\n===> RSService.GetIfNotModified"); - getRet = rs.GetIfNotModified(key, "attName", getRet.Hash); - PrintRet(getRet); - if (getRet.OK) - { - Console.WriteLine("Hash: " + getRet.Hash); - Console.WriteLine("FileSize: " + getRet.FileSize); - Console.WriteLine("MimeType: " + getRet.MimeType); - Console.WriteLine("Url: " + getRet.Url); - } - else - { - Console.WriteLine("Failed to GetIfNotModified"); - } - } - - public static void Stat(string key) - { - Console.WriteLine("\n===> RSService.Stat"); - StatRet statRet = rs.Stat(key); - PrintRet(statRet); - if (statRet.OK) - { - Console.WriteLine("Hash: " + statRet.Hash); - Console.WriteLine("FileSize: " + statRet.FileSize); - Console.WriteLine("PutTime: " + statRet.PutTime); - Console.WriteLine("MimeType: " + statRet.MimeType); - } - else - { - Console.WriteLine("Failed to Stat"); - } - } - - public static void Delete(string key) - { - Console.WriteLine("\n===> RSService.Delete"); - CallRet deleteRet = rs.Delete(key); - PrintRet(deleteRet); - if (!deleteRet.OK) - { - Console.WriteLine("Failed to Delete"); - } - } - - public static void Drop() - { - Console.WriteLine("\n===> RSService.Drop"); - CallRet dropRet = rs.Drop(); - PrintRet(dropRet); - if (!dropRet.OK) - { - Console.WriteLine("Failed to Drop"); - } - } - - public static void MakeDownloadToken() - { - Console.WriteLine("\n===> Auth.MakeDownloadToken"); - string pattern = "*/*"; - var downloadPolicy = new DownloadPolicy(pattern, 3600); - string dnToken = downloadPolicy.MakeAuthTokenString(); - Console.WriteLine("dnToken: " + dnToken); - } - - public static void ImageOps() - { - Console.WriteLine("\n===> FileOp.ImageInfo"); - ImageInfoRet infoRet = ImageOp.ImageInfo("http://" + DEMO_DOMAIN + "/" + key); - PrintRet(infoRet); - if (infoRet.OK) - { - Console.WriteLine("Format: " + infoRet.Format); - Console.WriteLine("Width: " + infoRet.Width); - Console.WriteLine("Heigth: " + infoRet.Height); - Console.WriteLine("ColorModel: " + infoRet.ColorModel); - } - else - { - Console.WriteLine("Failed to ImageInfo"); - } - - Console.WriteLine("\n===> FileOp.ImageExif"); - CallRet exifRet = ImageOp.ImageExif("http://" + DEMO_DOMAIN + "/" + key); - PrintRet(exifRet); - if (!exifRet.OK) - { - Console.WriteLine("Failed to ImageExif"); - } - - Console.WriteLine("\n===> FileOp.ImageViewUrl"); - ImageViewSpec viewSpec = new ImageViewSpec{Mode = 0, Width = 200, Height= 200}; - string viewUrl = ImageOp.ImageViewUrl("http://" + DEMO_DOMAIN + "/" + key, viewSpec); - Console.WriteLine("ImageViewUrl 1:" + viewUrl); - viewSpec.Quality = 1; - viewSpec.Format = "gif"; - viewUrl = ImageOp.ImageViewUrl("http://" + DEMO_DOMAIN + "/" + key, viewSpec); - Console.WriteLine("ImageViewUrl 2:" + viewUrl); - viewSpec.Quality = 90; - viewSpec.Sharpen = 10; - viewSpec.Format = "png"; - viewUrl = ImageOp.ImageViewUrl("http://" + DEMO_DOMAIN + "/" + key, viewSpec); - Console.WriteLine("ImageViewUrl 3:" + viewUrl); - - Console.WriteLine("\n===> FileOp.ImageMogrifyUrl"); - ImageMogrifySpec mogrSpec = new ImageMogrifySpec { - Thumbnail = "!50x50r", Gravity = "center", Rotate = 90, - Crop = "!50x50", Quality = 80, AutoOrient = true - }; - string mogrUrl = ImageOp.ImageMogrifyUrl("http://" + DEMO_DOMAIN + "/" + key, mogrSpec); - Console.WriteLine("ImageMogrifyUrl:" + mogrUrl); - - Console.WriteLine("\n===> Get"); - GetRet getRet = rs.Get(key, "save-as"); - PrintRet(getRet); - if (getRet.OK) - { - Console.WriteLine("Hash: " + getRet.Hash); - Console.WriteLine("FileSize: " + getRet.FileSize); - Console.WriteLine("MimeType: " + getRet.MimeType); - Console.WriteLine("Url: " + getRet.Url); - } - else - { - Console.WriteLine("Failed to Get"); - } - Console.WriteLine("\n===> FileOp.ImageMogrifySaveAs"); - PutFileRet saveAsRet = rs.ImageMogrifySaveAs(getRet.Url, mogrSpec, key + ".mogr-save-as"); - PrintRet(saveAsRet); - if (saveAsRet.OK) - { - Console.WriteLine("Hash: " + saveAsRet.Hash); - } - else - { - Console.WriteLine("Failed to ImageMogrifySaveAs"); - } - Console.WriteLine("\n===> Get"); - getRet = rs.Get(key + ".mogr-save-as", "mogr-save-as.jpg"); - PrintRet(getRet); - if (getRet.OK) - { - Console.WriteLine("Hash: " + getRet.Hash); - Console.WriteLine("FileSize: " + getRet.FileSize); - Console.WriteLine("MimeType: " + getRet.MimeType); - Console.WriteLine("Url: " + getRet.Url); - } - else - { - Console.WriteLine("Failed to Get"); - } - } - - public static void PrintRet(CallRet callRet) - { - Console.WriteLine("\n[CallRet]"); - Console.WriteLine("StatusCode: " + callRet.StatusCode.ToString()); - Console.WriteLine("Response:\n" + callRet.Response); - Console.WriteLine(); - } - } -} diff --git a/Demo/Demo.csproj b/Demo/Demo.csproj deleted file mode 100644 index abb28ab4..00000000 --- a/Demo/Demo.csproj +++ /dev/null @@ -1,61 +0,0 @@ - - - - Debug - x86 - 8.0.30703 - 2.0 - {8BA368D7-3784-4C50-9A28-4FA1A9C81555} - Exe - Properties - Demo - Demo - v4.0 - Client - 512 - - - x86 - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - x86 - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - Always - - - - - - - - {1C8C9909-57ED-44E4-81A5-0904E96FA00E} - QBox - - - - - - - - \ No newline at end of file diff --git a/Demo/Resource/gogopher.jpg b/Demo/Resource/gogopher.jpg deleted file mode 100644 index 12c4dab3..00000000 Binary files a/Demo/Resource/gogopher.jpg and /dev/null differ diff --git a/Docs/README.md b/Docs/README.md index 91877dc8..118adb83 100644 --- a/Docs/README.md +++ b/Docs/README.md @@ -2,371 +2,608 @@ title: CSharp SDK --- -此 SDK 适用于 .NET4 及以上版本。 -SDK 在这里:[https://github.com/qiniu/csharp-sdk/tags](https://github.com/qiniu/csharp-sdk/tags) +此 Charp SDK 适用于.net framework>4.0版本,基于 [七牛云存储官方API](http://docs.qiniu.com/) 构建。使用此 SDK 构建您的网络应用程序,能让您以非常便捷地方式将数据安全地存储到七牛云存储上。无论您的网络应用是一个网站程序,还是包括从云端(服务端程序)到终端(手持设备应用)的架构的服务或应用,通过七牛云存储及其 SDK,都能让您应用程序的终端用户高速上传和下载,同时也让您的服务端更加轻盈。 + +目录 +---- +- [1. 安装](#install) +- [2. 初始化](#setup) + - [2.1 配置密钥](#setup-key) +- [3. 资源管理接口](#rs-api) + - [3.1 查看单个文件属性信息](#rs-stat) + - [3.2 复制单个文件](#rs-copy) + - [3.3 移动单个文件](#rs-move) + - [3.4 删除单个文件](#rs-delete) + - [3.5 批量操作](#batch) + - [3.5.1 批量获取文件属性信息](#batch-stat) + - [3.5.2 批量复制文件](#batch-copy) + - [3.5.3 批量移动文件](#batch-move) + - [3.5.4 批量删除文件](#batch-delete) +- [4. 资源列表](#rsf-api) +- [5. 上传下载接口](#get-and-put-api) + - [5.1 上传授权](#token) + - [5.1.1 生成uptoken](#make-uptoken) + - [5.2 文件上传](#upload) + - [5.2.1 普通上传](#io-upload) + - [5.2.2 断点续上传](#resumable-io-upload) + - [5.3 文件下载](#io-download) + - [5.3.1 公有资源下载](#public-download) + - [5.3.2 私有资源下载](#private-download) +- [6. 数据处理接口](#fop-api) + - [6.1 图像](#fop-image) + - [6.1.1 查看图像属性](#fop-image-info) + - [6.1.2 查看图片EXIF信息](#fop-exif) + - [6.1.3 生成图片预览](#fop-image-view) + - [6.1.4 图片高级处理(缩略、裁剪、旋转、转化)](#fop-image-mogr) + - [6.1.5 图像水印接口](#fop-image-watermarker) +- [7. 贡献代码](#contribution) +- [8. 许可证](#license) + + +---- + + +## 1. 安装 +下载: + + git clone http://github.com/qiniu/csharp-sdk + +DLL引用方式: + + 下载DLL文件,右键<项目>-<引用>文件夹,在弹出的菜单中点击"添加引用"选项后弹出"添加引用"对话框,选择”浏览"Qiniu.DLL文件,点击确定 + +项目引用方式: + + 下载项目文件,右键解决方案,在弹出的菜单中点击"添加"->"现有项目",然后在弹出的对话框中选择 Qiniu.csproj"文件,点击确定。接下来与DLL引用方式类似,在"添加引用”对话框选择"项目"选项卡后选中Qiniu项目即可。 + +其它: + + CSharp SDK引用了第三方的开源项目 Json.NET,因此,您需要在项目中引用它 +项目地址:[http://json.codeplex.com](http://json.codeplex.com)。 + + +### 2.1 配置密钥 -**目录** - -- [1 接入](#turn-on) - - [配置密钥(AccessKey / SecretKey)](#establish_connection!) -- [2 授权](#auth) - - [2.1 上传文件授权](#auth-up) - - [2.2 下载私有文件授权](#auth-dn) - - [2.3 文件管理授权](#auth-mgr) -- [3 存储接口](#store) - - [3.1 上传文件](#up) - - [3.2 下载文件](#dn) - - [3.3 删除文件](#del) - - [3.4 获取文件信息](#stat) -- [4 文件处理接口](#fop) - - [4.1 图片处理](#imgfop) - - [4.1.1 获取图片基础信息](#imageinfo) - - [4.1.2 获取图片EXIF信息](#imageexif) - - [4.1.3 图片缩略图](#imageview) - - [4.1.4 高级图片处理](#imagemogrify) - - - - -## 1 接入 - - - -### 配置密钥(AccessKey / SecretKey) 要接入七牛云存储,您需要拥有一对有效的 Access Key 和 Secret Key 用来进行签名认证。可以通过如下步骤获得: 1. [开通七牛开发者帐号](https://portal.qiniu.com/signup) 2. [登录七牛开发者自助平台,查看 Access Key 和 Secret Key](https://portal.qiniu.com/setting/key) 。 -在获取到 Access Key 和 Secret Key 之后,您可以在您的程序中调用如下两行代码进行初始化对接: - - Config.ACCESS_KEY = ""; - Config.SECRET_KEY = ""; - -完整的代码示例参考 SDK 内置的 [Demo/Demo.cs](https://github.com/qiniu/csharp-sdk/blob/develop/Demo/Demo.cs) 文件。 - - - -## 2 授权 - - - -### 2.1 上传文件授权 - -上传文件需要 UpToken 来取得服务端授权。 -UpToken 是由 AuthPolicy 以及 AccessKey 和 SecretKey 生成的。 - - public class AuthPolicy +在获取到 Access Key 和 Secret Key 之后,您可以在您的程序中调用如下两行代码进行初始化对接, 要确保`ACCESS_KEY` 和 `SECRET_KEY` 在调用所有七牛API服务之前均已赋值: + +```c# +using Qiniu.Conf; +qiniu.conf.ACCESS_KEY = "" +qiniu.conf.SECRET_KEY = "" +``` + + +## 3. 资源管理接口 +基本的数据结构定义: + +```c# +// 摘要: +// 对bucket、key进行封装 +public class EntryPath +{ + public EntryPath(string bucket, string key); + + public string Base64EncodedURI { get; } + // + // 摘要: + // 七牛云存储空间名 + public string Bucket { get; } + // + // 摘要: + // 文件key + public string Key { get; } + // + // 摘要: + // bucket+ ":"+ key + public string URI { get; } +} +// 摘要: +// 二元操作路径 +public class EntryPathPair +{ + // 摘要: + // 二元操作路径构造函数 + // 参数: + // bucket: + // 源空间名称,目标空间名称 + // keySrc: + // 源文件key + // keyDest: + // 目标文件key + public EntryPathPair(string bucket, string keySrc, string keyDest); + // + // 摘要: + // 二元操作路径构造函数 + // 参数: + // bucketSrc: + // 源空间名称 + // keySrc: + // 源文件key + // bucketDest: + // 目标空间名称 + // keyDest: + // 目标文件key + public EntryPathPair(string bucketSrc, string keySrc, string bucketDest, string keyDest); + + // 摘要: + // bucketDest+":"+keyDest + public string URIDest { get; } + // + // 摘要: + // bucketSrc+":"+keySrc + public string URISrc { get; } +} +``` + + +### 3.1 查看单个文件属性信息 + +```c# +//example +using Qiniu.RS + +/// +/// 查看单个文件属性信息 +/// +/// 七牛云存储空间名 +/// 文件key +public static void Stat(string bucket, string key) +{ + RSClient client = new RSClient(); + Entry entry = client.Stat(new EntryPath(bucket, key)); + if (entry.OK) { - public string Scope { get; set; } - public long Deadline { get; set; } - public string CallbackUrl { get; set; } - public string CallbackBodyType { get; set; } - public bool Escape { get; set; } - public string AsyncOps { get; set; } - public string ReturnBody { get; set; } - } - -各字段的含义见[这里](http://docs.qiniu.com/api/put.html#uploadToken)。 - -生成 UpToken 例子: - - using QBox.Auth; - - var authPolicy = new AuthPolicy(bucketName, 3600); - authPolicy.CallbackUrl = "www.example.com/qiniu/callback"; - authPolicy.CallbackBodyType = "application/json" - string upToken = authPolicy.MakeAuthTokenString(); - - - -### 2.2 下载私有文件授权 - -下载私有文件需要 DownloadToken 来取得服务端授权。 -DownloadToken 是由 DownloadPolicy 以及 AccessKey 和 SecretKey 生成的。 - - public class DownloadPolicy - { - public string Pattern { get; set; } - public long Deadline { get; set; } - } - -各参数的含义见[这里](http://docs.qiniu.com/api/get.html#download-token)。 - -生成 DownloadToken 例子: - - using QBox.Auth; - - string pattern = "dl.example.com/*"; - var downloadPolicy = new DownloadPolicy(pattern, 3600); - string downloadToken = downloadPolicy.MakeAuthTokenString(); - - - -### 2.3 文件管理授权 - -文件管理,比如删除文件,获取文件元信息等操作需要提供 AccessToken(放在 HTTP Header 里面) 来取得服务端授权。 -AccessToken 是由 HTTP 请求的 URL,BodyType,Body 以及 AccessKey 和 SecretKey 生成的。 - -获取自动为请求添加 AccessToken 的客户端: - - using QBox.Auth; - using QBox.RS; - - conn = new DigestAuthClient(); - rs = new RSService(conn, bucketName); - -然后就可以用 rs 来进行文件管理操作。 - - - -## 3 存储接口 - - - -### 3.1 上传文件 - -上传文件需要 upToken,上传 API 为: - - public static PutFileRet PutFileWithUpToken( - string upToken, string tableName, string key, string mimeType, - string localFile, string customMeta, string callbackParam) - -例子: - - using QBox.Auth; - using QBox.RS; - - var authPolicy = new AuthPolicy(bucketName, 3600); - authPolicy.CallbackUrl = "www.example.com/qiniu/callback"; - authPolicy.CallbackBodyType = "application/json" - string upToken = authPolicy.MakeAuthTokenString(); - - string callbackParam = "bucket=&key=" - PutFileRet ret = RSClient.PutFileWithUpToken(upToken, tableName, key, null, localFile, null, callbackParam); - if (ret.OK) Console.Writeline("upload and callback ok"); - -此例子是上传一个文件然后将上传的 bucket 和 key 信息回调给 www.example.com/qiniu/callback。 - -如果上传的文件比较大(大于4M),可以使用断点续传,其将文件在内部切割成多个 4M 的块, -一块块上传,以免直接上传出现超时或用户体验差的问题,断点续传 API 为: - - public static PutFileRet PutFile( - Client client, string tableName, string key, string mimeType, - string localFile, string customMeta, string callbackParam) - -client 参数是能自动为请求在 HTTP Header 中添加 UpToken 的 Client。 - -例子: - - using QBox.Auth; - using QBox.RS; - - var authPolicy = new AuthPolicy(bucketName, 3600); - string upToken = authPolicy.MakeAuthTokenString(); - PutAuthClient client = new PutAuthClient(upToken); - - PutFileRet ret = ResumablePut.PutFile(client, tableName, key, null, bigFile, null, null); - if (ret.OK) Console.Writeline("resumable put ok"); - - - -### 3.2 下载文件 - -对于公有资源,访问方式为: - - http://<绑定域名>/key - -对于[私有资源](http://docs.qiniu.com/api/get.html#private-download),需要 downloadToken,访问方式为: - - http://<绑定域名>/key?token= - - - -### 3.3 删除文件 - -需要 AccessToken 授权,删除 API 为: - - public CallRet Delete(string key) - -例子: - - using QBox.Auth; - using QBox.RS; - - conn = new DigestAuthClient(); - rs = new RSService(conn, bucketName); - CallRet ret = rs.Delete(key); - if (ret.OK) Console.Write("delete ok"); - - - -### 3.4 获取文件元信息 - -需要 AccessToken 授权,获取元信息 API 为: - - public class StatRet : CallRet - { - public string Hash { get; private set; } - public long FileSize { get; private set; } - public long PutTime { get; private set; } - public string MimeType { get; private set; } - } - public StatRet Stat(string key); - -例子: - - using QBox.Auth; - using QBox.RS; - - conn = new DigestAuthClient(); - rs = new RSService(conn, bucketName); - StatRet ret = rs.Stat(key); - if (ret.OK) - { - Console.WriteLine("Hash: " + ret.Hash); - Console.WriteLine("FileSize: " + ret.FileSize); - Console.WriteLine("PutTime: " + ret.PutTime); - Console.WriteLine("MimeType: " + ret.MimeType); - } - - - -## 4 文件处理接口 - - - -### 4.1 图像处理 - - - -#### 4.1.1 获取图片信息 - -获取图片基本信息,API 为: - - public class ImageInfoRet : CallRet - { - public string Format { get; private set; } - public int Width { get; private set; } - public int Height { get; private set; } - public string ColorModel { get; private set; } - } - public static ImageInfoRet ImageInfo(string url); - -例子: - - using QBox.FileOp; - - ImageInfoRet ret = ImageOp.ImageInfo("http://yourbucket.qiniudn.com/" + key); + 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"); + } +} +``` + + +### 3.2 复制单个文件 + +```c# +//example + +/// +/// 复制单个文件 +/// +/// 需要复制的文件所在的空间名 +/// 需要复制的文件key +/// 目标文件所在的空间名 +/// 标文件key +public static void Copy(string bucketSrc, string keySrc, string bucketDest, string keyDest) +{ + RSClient client = new RSClient(); + CallRet ret = client.Copy(new EntryPathPair(bucketSrc, keySrc, bucketDest, keyDest)); + if (ret.OK) + { + Console.WriteLine("Copy OK"); + } + else + { + Console.WriteLine("Failed to Copy"); + } +} +``` + + +### 3.3 移动单个文件 + +```c# +//example + +/// +/// 移动单个文件 +/// +/// 需要移动的文件所在的空间名 +/// 需要移动的文件 +/// 目标文件所在的空间名 +/// 目标文件key +public static void Move(string bucketSrc, string keySrc, string bucketDest, string keyDest) +{ + Console.WriteLine("\n===> Move {0}:{1} To {2}:{3}", + bucketSrc, keySrc, bucketDest, keyDest); + RSClient client = new RSClient(); + new EntryPathPair(bucketSrc, keySrc, bucketDest, keyDest); + CallRet ret = client.Move(new EntryPathPair(bucketSrc, keySrc, bucketDest, keyDest)); + if (ret.OK) + { + Console.WriteLine("Move OK"); + } + else + { + Console.WriteLine("Failed to Move"); + } +} +``` + + +### 3.4 删除单个文件 + +```C# +//example + +/// +/// 删除单个文件 +/// +/// 文件所在的空间名 +/// 文件key +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("Format: " + ret.Format); - Console.WriteLine("Width: " + ret.Width); - Console.WriteLine("Heigth: " + ret.Height); - Console.WriteLine("ColorModel: " + ret.ColorModel); - } - - - -#### 4.1.2 获取图片EXIF信息 - -获取图片 EXIF 信息,API 为: - - public static CallRet ImageExif(string url); - -例子: - - using QBox.FileOp; - using QBox.RPC; - - CallRet ret = ImageOp.ImageExif("http://yourbucket.qiniudn.com/" + key); - if (ret.OK) Console.Writeline("Exif:\n" + ret.Response); - - - -#### 4.1.3 图片缩略图 - -获取缩略图URL,API 为: - - public class ImageViewSpec - { - public int Mode { get; set; } - public int Width { get; set; } - public int Height { get; set; } - public int Quality { get; set; } - public string Format { get; set; } - public int Sharpen { get; set; } - - public string MakeSpecString() - } - -具体字段含义见[这里](http://docs.qiniu.com/api/image-process.html#imageView) - -例子: - - using QBox.FileOp; - - ImageViewSpec viewSpec = new ImageViewSpec{Mode = 1, Width = 200, Height= 200}; - string viewUrl = ImageOp.ImageViewUrl("http://yourbucket.qiniudn.com/" + key, viewSpec); - Console.WriteLine("ImageViewUrl:" + viewUrl); - - - -#### 4.1.4 高级图片处理 - -可以对存储中的图片做缩略、裁剪、旋转和格式转化处理,API 为: - - public class ImageMogrifySpec - { - public string Thumbnail { get; set; } - public string Gravity { get; set; } - public string Crop { get; set; } - public int Quality { get; set; } - public int Rotate { get; set; } - public string Format { get; set; } - public bool AutoOrient { get; set; } - - public string MakeSpecString() - } - -具体字段含义见[这里](http://docs.qiniu.com/api/image-process.html#imageMogr)。 - -例子: - - using QBox.FileOp; - - ImageMogrifySpec mogrSpec = new ImageMogrifySpec { - Thumbnail = "!50x50r", Gravity = "center", Rotate = 90, - Crop = "!50x50", Quality = 80, AutoOrient = true}; - string mogrUrl = ImageOp.ImageMogrifyUrl("http://yourbucket.qiniudn.com/" + key, mogrSpec); - Console.WriteLine("ImageMogrifyUrl:" + mogrUrl); - -可以将处理后的图片持久化到云存储,这里需要一个结过授权的图片 URL, 可以用 Get 接口获取的,所需 API 为: - - public class GetRet : CallRet - { - public string Hash { get; private set; } - public long FileSize { get; private set; } - public string MimeType { get; private set; } - public string Url { get; private set; } - } - public GetRet Get(string key, string attName); - public PutFileRet ImageMogrifySaveAs(string url, ImageMogrifySpec spec, string key) - -例子: - - using QBox.Auth; - using QBox.RS; - using QBox.FileOp; - - conn = new DigestAuthClient(); - rs = new RSService(conn, bucketName); - GetRet getRet = rs.Get(key, "save-as"); - - if (getRet.OK) - { - PutFileRet saveAsRet = rs.ImageMogrifySaveAs(getRet.Url, mogrSpec, key + ".save-as.jpg"); - if (saveAsRet.OK) Console.Writeline("mogrify ok and save to :.save-as.jpg"); + Console.WriteLine("Delete OK"); } + else + { + Console.WriteLine("Failed to delete"); + } +} +``` + + +### 3.5 批量操作 +当您需要一次性进行多个操作时, 可以使用批量操作. + +#### 3.5.1 批量获取文件属性信息 + +```C# +//example + +public static void BatchStat(string bucket, string[] keys) +{ + RSClient client = new RSClient(); + List EntryPaths= new List(); + foreach(string key in keys) + { + Console.WriteLine("\n===> Stat {0}:{1}", bucket, key); + EntryPaths.Add(new EntryPath(bucket,key)); + } + client.BatchStat(EntryPaths.ToArray()); +} +``` + + +#### 3.5.2 批量复制文件 + +```C# +//example + +public static void BatchCopy(string bucket, string[] keys) +{ + List pairs = new List(); + foreach (string key in keys) + { + EntryPathPair entry = new EntryPathPair(bucket, key, Guid.NewGuid().ToString()); + pairs.Add(entry); + } + RSClient client = new RSClient(); + client.BatchCopy(pairs.ToArray()); +} +``` + + +#### 3.5.3 批量移动文件 + +```c# +//example + +public static void BatchMove(string bucket, string[] keys) +{ + List pairs = new List(); + foreach (string key in keys) + { + EntryPathPair entry = new EntryPathPair(bucket, key, Guid.NewGuid().ToString()); + pairs.Add(entry); + } + RSClient client = new RSClient(); + client.BatchMove(pairs.ToArray()); +} +``` + + +#### 3.5.4 批量删除文件 + +```c# +//example + +public static void BatchDelete(string bucket, string[] keys) +{ + RSClient client = new RSClient(); + List EntryPaths = new List(); + foreach (string key in keys) + { + Console.WriteLine("\n===> Stat {0}:{1}", bucket, key); + EntryPaths.Add(new EntryPath(bucket, key)); + } + client.BatchDelete(EntryPaths.ToArray()); +} +``` + + +##4. 资源列表 +资源列表接口允许用户列出空间下的所有文件信息。使用资源列表接口如果引入Qiniu.RSF命名空间。 + +```c# + using Qiniu.RSF; + RSFClient client = new RSFClient(); + client.listPrifix("", "", "", 500); +``` + +或者, + +```c# +public static void List (string bucket) +{ + RSF.RSFClient rsf = new Qiniu.RSF.RSFClient(bucket); + rsf.Prefix = "test"; + rsf.Limit = 100; + List items; + while ((items=rsf.Next())!=null) + { + //todo + } +} +``` + + +## 5. 上传下载接口 + + +### 5.1 上传下载授权 + +#### 5.1.1 上传授权uptoken +uptoken是一个字符串,作为http协议Header的一部分(Authorization字段)发送到我们七牛的服务端,表示这个http请求是经过认证的。 + +```c# +PutPolicy put = new PutPolicy(bucketName); +put.Token(); +``` + + +### 5.2 文件上传 +**注意**:如果您只是想要上传已存在您电脑本地或者是服务器上的文件到七牛云存储,可以直接使用七牛提供的 [qrsync](/tools/qrsync.html/) 上传工具。 +文件上传有两种方式,一种是以普通方式直传文件,简称普通上传,另一种方式是断点续上传,断点续上传在网络条件很一般的情况下也能有出色的上传速度,而且对大文件的传输非常友好。 + + +### 5.2.1 普通上传 +普通上传的接口在 `qiniu.io` 里,如下: + +上传本地文件 + +```c# +/// +/// 上传文件测试 +/// +/// +/// +/// +public static void PutFile(string bucket, string key, string fname) +{ + var policy = new PutPolicy(bucket, 3600); + string upToken = policy.Token(); + PutExtra extra = new PutExtra { Bucket = bucket }; + IOClient client = new IOClient(); + client.PutFinished += new EventHandler((o, ret) => { + if (ret.OK) + { + Console.WriteLine("Hash: " + ret.Hash); + } + else + { + Console.WriteLine("Failed to PutFile"); + } + }); + client.PutFile(upToken, key, fname, extra); +} +``` + +为防止在上传较大文件时发生GUI界面出现假死现像,c# SDK的内部被设计为异步上传模式,您可以通过注册client的PutFinished事件获取上传结果。该事件无论上传是否会成功,都会被触发。 + + + + +### 5.2.2 断点续上传 + +上传本地文件 + +```c# +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(); + extra.Bucket = bucket; + ResumablePut client = new ResumablePut(setting, extra); + client.Progress += new Action((p) => { + Console.WriteLine("当前进度:{0}%", p * 100); + + }); + client.PutFinished += new EventHandler((o, ret) => { + if (ret.OK) + { + Console.WriteLine("上传成功:{0}",ret.Response); + } + else + { + Console.WriteLine("上传失败:{0}", ret.Response); + } + }); + client.PutFile(upToken, fname, Guid.NewGuid().ToString()); +} +``` + +ResumablePut采用分快上传,各快之间采用并行上传,通过注册事件Progress可以获取当前文件上传进度,同时您也可以通过注册ResumablePutExtra以下两个事件监听当前上传进度以及成功情况: + +```c# +public event EventHandler Notify; +public event EventHandler NotifyErr; +``` + + +### 5.3 文件下载 +七牛云存储上的资源下载分为 公有资源下载 和 私有资源下载 。 + +私有(private)是 Bucket(空间)的一个属性,一个私有 Bucket 中的资源为私有资源,私有资源不可匿名下载。 + +新创建的空间(Bucket)缺省为私有,也可以将某个 Bucket 设为公有,公有 Bucket 中的资源为公有资源,公有资源可以匿名下载。 + + +#### 5.3.1 公有资源下载 +如果在给bucket绑定了域名的话,可以通过以下地址访问。 + + [GET] http:/// + +其中可以到[七牛云存储开发者自助网站](https://portal.qiniu.com/)绑定, 域名可以使用自己一级域名的或者是由七牛提供的二级域名(`.qiniudn.com`)。注意,尖括号不是必需,代表替换项。 + + +#### 5.3.2 私有资源下载 +私有资源必须通过临时下载授权凭证(downloadToken)下载,如下: + + [GET] http:///?e=&token= + +注意,尖括号不是必需,代表替换项。 +私有下载链接可以使用 SDK 提供的如下方法生成: + +```c# +using Qiniu.RS; +public static void MakeGetToken(string domain, string key) +{ + string baseUrl = GetPolicy.MakeBaseUrl(domain, key); + string private_url = GetPolicy.MakeRequest(baseUrl); +} +``` + + +## 6. 数据处理接口 +七牛支持在云端对图像, 视频, 音频等富媒体进行个性化处理。使用数据处理接口需要引入Qiniu.FileOp命名空间。 + +```c# +using Qiniu.FileOp; +``` + + +### 6.1 图像 + +### 6.1.1 查看图像属性 + +```c# + string domain = "domain"; + string key = key; + Console.WriteLine("\n===> FileOp.ImageInfo"); + //生成base_url + string url = Qiniu.RS.GetPolicy.MakeBaseUrl(domian, key); + //生成fop_url + string imageInfoURL = ImageInfo.MakeRequest(url); + //对其签名,生成private_url。如果是公有bucket此步可以省略 + imageInfoURL = GetPolicy.MakeRequest(imageInfoURL); + ImageInfoRet infoRet = ImageInfo.Call(imageInfoURL); + if (infoRet.OK) + { + Console.WriteLine("Format: " + infoRet.Format); + Console.WriteLine("Width: " + infoRet.Width); + Console.WriteLine("Heigth: " + infoRet.Height); + Console.WriteLine("ColorModel: " + infoRet.ColorModel); + } + else + { + Console.WriteLine("Failed to ImageInfo"); + } +``` + + +### 6.1.2 查看图片EXIF信息 + +```C# + string exifURL = Exif.MakeRequest(url); + ExifRet exifRet = Exif.Call(exifURL); + if (exifRet.OK) + { + Console.WriteLine("ApertureValue.val: " + exifRet["ApertureValue"].val); + Console.WriteLine("ApertureValue.type: " + exifRet["ApertureValue"].type.ToString()); + Console.WriteLine("ExifInfo: " + exifRet.ToString()); + } + else + { + Console.WriteLine("Failed to ImageExif"); + } +``` + + + +### 6.1.3 生成图片预览 + +```c# + ImageView imageView = new ImageView { Mode = 0, Width = 200, Height = 200, Quality = 90, Format = "gif" }; + string viewUrl = imageView.MakeRequest(url); + viewUrl = GetPolicy.MakeRequest(viewUrl); + Console.WriteLine("ImageViewURL:" + viewUrl); +``` + + +### 6.1.4 图片高级处理(缩略、裁剪、旋转、转化) + +```c# + ImageMogrify imageMogr = new ImageMogrify + { + Thumbnail = "!50x50r", + Gravity = "center", + Rotate = 90, + Crop = "!50x50", + Quality = 80, + AutoOrient = true + }; + string mogrUrl = imageMogr.MakeRequest(url); + mogrUrl = GetPolicy.MakeRequest(mogrUrl); + Console.WriteLine("ImageMogrifyURL:" + mogrUrl); +``` + + +### 6.1.5 图像水印接口 + +```c# + //文字水印 + WaterMarker marker = new TextWaterMarker("hello,qiniu cloud!","","red"); + string MarkerUrl = marker.MakeRequest(url); + //图片水印 + marker = new ImageWaterMarker("http://www.b1.qiniudn.com/images/logo-2.png"); + MarkerUrl = marker.MakeRequest(url); +``` + + +## 7. 贡献代码 + +1. Fork +2. 创建您的特性分支 (`git checkout -b my-new-feature`) +3. 提交您的改动 (`git commit -am 'Added some feature'`) +4. 将您的修改记录提交到远程 `git` 仓库 (`git push origin my-new-feature`) +5. 然后到 github 网站的该 `git` 远程仓库的 `my-new-feature` 分支下发起 Pull Request + + +## 8. 许可证 + +Copyright (c) 2013 qiniu.com + +基于 MIT 协议发布: + +* [www.opensource.org/licenses/MIT](http://www.opensource.org/licenses/MIT) \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..7992b4a0 --- /dev/null +++ b/Makefile @@ -0,0 +1,15 @@ +# for travis ci +all: + + xbuild csharp-sdk.sln; + cp Qiniu.Test/bin/Debug/Qiniu.Test.dll bin + cp Qiniu/bin/Debug/Qiniu.dll bin + +test: + + cp tools/Newtonsoft.Json.dll tools/nunit.framework.dll bin + #for OS X + #export MON_PATH="/Library/Frameworks/Mono.framework/Libraries/mono/4.0/" + #mono --debug /Library/Frameworks/Mono.framework/Versions/3.2.0/lib/mono/4.5/nunit-console.exe bin/Qiniu.Test.dll + #for Linux + nunit-console -framework="4.0" bin/Qiniu.Test.dll diff --git a/QBox/Auth/AuthPolicy.cs b/QBox/Auth/AuthPolicy.cs deleted file mode 100644 index 1bd4a9c6..00000000 --- a/QBox/Auth/AuthPolicy.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Text; -using System.IO; -using LitJson; -using System.Security.Cryptography; -using QBox.RS; -using QBox.Util; - -namespace QBox.Auth -{ - public class AuthPolicy - { - public string Scope { get; set; } - public long Deadline { get; set; } - public string CallbackUrl { get; set; } - public string CallbackBodyType { get; set; } - public bool Escape { get; set; } - public string AsyncOps { get; set; } - public string ReturnBody { get; set; } - - public AuthPolicy(string scope, long expires) - { - Scope = scope; - DateTime begin = new DateTime(1970, 1, 1); - DateTime now = DateTime.Now; - TimeSpan interval = new TimeSpan(now.Ticks - begin.Ticks); - Deadline = (long)interval.TotalSeconds + expires; - } - - public string Marshal() - { - JsonData data = new JsonData(); - data["scope"] = Scope; - data["deadline"] = Deadline; - if (!String.IsNullOrEmpty(CallbackUrl)) - data["callbackUrl"] = CallbackUrl; - if (!String.IsNullOrEmpty(CallbackBodyType)) - data["callbackBodyType"] = CallbackBodyType; - if (Escape) - data["escape"] = 1; - if (!String.IsNullOrEmpty(AsyncOps)) - data["asyncOps"] = AsyncOps; - if (!String.IsNullOrEmpty(ReturnBody)) - data["returnBody"] = ReturnBody; - return data.ToJson(); - } - - public byte[] MakeAuthToken() - { - return AuthToken.Make(Marshal()); - } - - public string MakeAuthTokenString() - { - return Encoding.ASCII.GetString(MakeAuthToken()); - } - } -} diff --git a/QBox/Auth/AuthToken.cs b/QBox/Auth/AuthToken.cs deleted file mode 100644 index ecc02b75..00000000 --- a/QBox/Auth/AuthToken.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Text; -using System.IO; -using System.Security.Cryptography; -using QBox.RS; -using QBox.Util; - -namespace QBox.Auth -{ - public static class AuthToken - { - public static byte[] Make(string scope) - { - Encoding encoding = Encoding.ASCII; - byte[] accessKey = encoding.GetBytes(Config.ACCESS_KEY); - byte[] secretKey = encoding.GetBytes(Config.SECRET_KEY); - byte[] upToken = null; - try - { - byte[] policyBase64 = encoding.GetBytes(Base64UrlSafe.Encode(scope)); - byte[] digestBase64 = null; - using (HMACSHA1 hmac = new HMACSHA1(secretKey)) - { - byte[] digest = hmac.ComputeHash(policyBase64); - digestBase64 = encoding.GetBytes(Base64UrlSafe.Encode(digest)); - } - using (MemoryStream buffer = new MemoryStream()) - { - buffer.Write(accessKey, 0, accessKey.Length); - buffer.WriteByte((byte)':'); - buffer.Write(digestBase64, 0, digestBase64.Length); - buffer.WriteByte((byte)':'); - buffer.Write(policyBase64, 0, policyBase64.Length); - upToken = buffer.ToArray(); - } - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - } - return upToken; - } - - } -} diff --git a/QBox/Auth/DigestAuthClient.cs b/QBox/Auth/DigestAuthClient.cs deleted file mode 100644 index e156c270..00000000 --- a/QBox/Auth/DigestAuthClient.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Text; -using System.Net; -using System.IO; -using System.Security.Cryptography; -using QBox.Util; -using QBox.RPC; -using QBox.RS; - -namespace QBox.Auth -{ - public class DigestAuthClient : Client - { - public override void SetAuth(HttpWebRequest request, Stream body) - { - byte[] secretKey = Encoding.ASCII.GetBytes(Config.SECRET_KEY); - using (HMACSHA1 hmac = new HMACSHA1(secretKey)) - { - string pathAndQuery = request.Address.PathAndQuery; - byte[] pathAndQueryBytes = Encoding.ASCII.GetBytes(pathAndQuery); - using (MemoryStream buffer = new MemoryStream()) - { - buffer.Write(pathAndQueryBytes, 0, pathAndQueryBytes.Length); - buffer.WriteByte((byte)'\n'); - if (request.ContentType == "application/x-www-form-urlencoded" && body != null) - { - if (!body.CanSeek) - { - throw new Exception("stream can not seek"); - } - StreamUtil.Copy(body, buffer); - body.Seek(0, SeekOrigin.Begin); - } - byte[] digest = hmac.ComputeHash(buffer.ToArray()); - string digestBase64 = Base64UrlSafe.Encode(digest); - - string authHead = "QBox " + Config.ACCESS_KEY + ":" + digestBase64; - request.Headers.Add("Authorization", authHead); - } - } - } - } -} diff --git a/QBox/Auth/DownloadPolicy.cs b/QBox/Auth/DownloadPolicy.cs deleted file mode 100644 index 219a152a..00000000 --- a/QBox/Auth/DownloadPolicy.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Text; -using System.IO; -using System.Security.Cryptography; -using LitJson; -using QBox.RS; -using QBox.Util; - -namespace QBox.Auth -{ - public class DownloadPolicy - { - public string Pattern { get; set; } - public long Deadline { get; set; } - - public DownloadPolicy(string pattern, long expires) - { - Pattern = pattern; - DateTime begin = new DateTime(1970, 1, 1); - DateTime now = DateTime.Now; - TimeSpan interval = new TimeSpan(now.Ticks - begin.Ticks); - Deadline = (long)interval.TotalSeconds + expires; - } - - public string Marshal() - { - JsonData data = new JsonData(); - data["S"] = Pattern; - data["E"] = Deadline; - return data.ToJson(); - } - - public byte[] MakeAuthToken() - { - return AuthToken.Make(Marshal()); - } - - public string MakeAuthTokenString() - { - return Encoding.ASCII.GetString(MakeAuthToken()); - } - } -} diff --git a/QBox/Auth/PutAuthClient.cs b/QBox/Auth/PutAuthClient.cs deleted file mode 100644 index 5e489f21..00000000 --- a/QBox/Auth/PutAuthClient.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Net; -using System.IO; -using QBox.RPC; - -namespace QBox.Auth -{ - public class PutAuthClient : Client - { - public string UpToken { get; set; } - - public PutAuthClient(string upToken) - { - UpToken = upToken; - } - - public override void SetAuth(HttpWebRequest request, Stream body) - { - string authHead = "UpToken " + UpToken; - request.Headers.Add("Authorization", authHead); - } - } -} diff --git a/QBox/FileOp/FileOpClient.cs b/QBox/FileOp/FileOpClient.cs deleted file mode 100644 index 55fc1c5c..00000000 --- a/QBox/FileOp/FileOpClient.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Net; -using System.IO; -using QBox.RPC; - -namespace QBox.FileOp -{ - public static class FileOpClient - { - public static CallRet Get(string url) - { - Console.WriteLine("Client.Get ==> URL: " + url); - try - { - HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); - request.Method = "GET"; - using (HttpWebResponse response = request.GetResponse() as HttpWebResponse) - { - return HandleResult(response); - } - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - return new CallRet(HttpStatusCode.BadRequest, e); - } - } - - public static CallRet HandleResult(HttpWebResponse response) - { - HttpStatusCode statusCode = response.StatusCode; - using (StreamReader reader = new StreamReader(response.GetResponseStream())) - { - string responseStr = reader.ReadToEnd(); - return new CallRet(statusCode, responseStr); - } - } - } -} diff --git a/QBox/FileOp/ImageInfoRet.cs b/QBox/FileOp/ImageInfoRet.cs deleted file mode 100644 index d83afee5..00000000 --- a/QBox/FileOp/ImageInfoRet.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using LitJson; -using QBox.RPC; - -namespace QBox.FileOp -{ - public class ImageInfoRet : CallRet - { - public string Format { get; private set; } - public int Width { get; private set; } - public int Height { get; private set; } - public string ColorModel { get; private set; } - - public ImageInfoRet(CallRet ret) - : base(ret) - { - if (!String.IsNullOrEmpty(Response)) - { - try - { - Unmarshal(Response); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - this.Exception = e; - } - } - } - - private void Unmarshal(string json) - { - JsonData data = JsonMapper.ToObject(json); - Format = (string)data["format"]; - Width = (int)data["width"]; - Height = (int)data["height"]; - ColorModel = (string)data["colorModel"]; - } - } -} diff --git a/QBox/FileOp/ImageMogrifySpec.cs b/QBox/FileOp/ImageMogrifySpec.cs deleted file mode 100644 index 338f26e7..00000000 --- a/QBox/FileOp/ImageMogrifySpec.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; - -namespace QBox.FileOp -{ - public class ImageMogrifySpec - { - public string Thumbnail { get; set; } - public string Gravity { get; set; } - public string Crop { get; set; } - public int Quality { get; set; } - public int Rotate { get; set; } - public string Format { get; set; } - public bool AutoOrient { get; set; } - - public string MakeSpecString() - { - string spec = "?imageMogr"; - if (!String.IsNullOrEmpty(Thumbnail)) - spec += "/thumbnail/" + Thumbnail; - if (!String.IsNullOrEmpty(Gravity)) - spec += "/gravity/" + Gravity; - if (!String.IsNullOrEmpty(Crop)) - spec += "/crop/" + Crop; - if (Quality != 0) - spec += "/quality/" + Quality.ToString(); - if (Rotate != 0) - spec += "/rotate/" + Rotate.ToString(); - if (!String.IsNullOrEmpty(Format)) - spec += "/format/" + Format; - if (AutoOrient) - spec += "/auto-orient"; - return spec; - } - } -} diff --git a/QBox/FileOp/ImageOp.cs b/QBox/FileOp/ImageOp.cs deleted file mode 100644 index 7fccb45f..00000000 --- a/QBox/FileOp/ImageOp.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using QBox.RPC; - -namespace QBox.FileOp -{ - public static class ImageOp - { - public static ImageInfoRet ImageInfo(string url) - { - CallRet callRet = FileOpClient.Get(url + "?imageInfo"); - return new ImageInfoRet(callRet); - } - - public static CallRet ImageExif(string url) - { - return FileOpClient.Get(url + "?exif"); - } - - public static string ImageViewUrl(string url, ImageViewSpec spec) - { - return url + spec.MakeSpecString(); - } - - public static string ImageMogrifyUrl(string url, ImageMogrifySpec spec) - { - return url + spec.MakeSpecString(); - } - } -} diff --git a/QBox/FileOp/ImageViewSpec.cs b/QBox/FileOp/ImageViewSpec.cs deleted file mode 100644 index df05bd28..00000000 --- a/QBox/FileOp/ImageViewSpec.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; - -namespace QBox.FileOp -{ - public class ImageViewSpec - { - public int Mode { get; set; } - public int Width { get; set; } - public int Height { get; set; } - public int Quality { get; set; } - public string Format { get; set; } - public int Sharpen { get; set; } - - public string MakeSpecString() - { - string spec = "?imageView/" + Mode.ToString(); - if (Width != 0) - spec += "/w/" + Width.ToString(); - if (Height != 0) - spec += "/h/" + Height.ToString(); - if (Quality != 0) - spec += "/q/" + Quality.ToString(); - if (!String.IsNullOrEmpty(Format)) - spec += "/format/" + Format; - if (Sharpen != 0) - spec += "/sharpen/" + Sharpen; - return spec; - } - } -} diff --git a/QBox/Json/IJsonWrapper.cs b/QBox/Json/IJsonWrapper.cs deleted file mode 100644 index 2acebede..00000000 --- a/QBox/Json/IJsonWrapper.cs +++ /dev/null @@ -1,60 +0,0 @@ -#region Header -/** - * IJsonWrapper.cs - * Interface that represents a type capable of handling all kinds of JSON - * data. This is mainly used when mapping objects through JsonMapper, and - * it's implemented by JsonData. - * - * The authors disclaim copyright to this source code. For more details, see - * the COPYING file included with this distribution. - **/ -#endregion - - -using System.Collections; -using System.Collections.Specialized; - - -namespace LitJson -{ - public enum JsonType - { - None, - - Object, - Array, - String, - Int, - Long, - Double, - Boolean - } - - public interface IJsonWrapper : IList, IOrderedDictionary - { - bool IsArray { get; } - bool IsBoolean { get; } - bool IsDouble { get; } - bool IsInt { get; } - bool IsLong { get; } - bool IsObject { get; } - bool IsString { get; } - - bool GetBoolean (); - double GetDouble (); - int GetInt (); - JsonType GetJsonType (); - long GetLong (); - string GetString (); - - void SetBoolean (bool val); - void SetDouble (double val); - void SetInt (int val); - void SetJsonType (JsonType type); - void SetLong (long val); - void SetString (string val); - - string ToJson (); - void ToJson (JsonWriter writer); - } -} diff --git a/QBox/Json/JsonData.cs b/QBox/Json/JsonData.cs deleted file mode 100644 index b0ca03cc..00000000 --- a/QBox/Json/JsonData.cs +++ /dev/null @@ -1,993 +0,0 @@ -#region Header -/** - * JsonData.cs - * Generic type to hold JSON data (objects, arrays, and so on). This is - * the default type returned by JsonMapper.ToObject(). - * - * The authors disclaim copyright to this source code. For more details, see - * the COPYING file included with this distribution. - **/ -#endregion - - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.IO; - - -namespace LitJson -{ - public class JsonData : IJsonWrapper, IEquatable - { - #region Fields - private IList inst_array; - private bool inst_boolean; - private double inst_double; - private int inst_int; - private long inst_long; - private IDictionary inst_object; - private string inst_string; - private string json; - private JsonType type; - - // Used to implement the IOrderedDictionary interface - private IList> object_list; - #endregion - - - #region Properties - public int Count { - get { return EnsureCollection ().Count; } - } - - public bool IsArray { - get { return type == JsonType.Array; } - } - - public bool IsBoolean { - get { return type == JsonType.Boolean; } - } - - public bool IsDouble { - get { return type == JsonType.Double; } - } - - public bool IsInt { - get { return type == JsonType.Int; } - } - - public bool IsLong { - get { return type == JsonType.Long; } - } - - public bool IsObject { - get { return type == JsonType.Object; } - } - - public bool IsString { - get { return type == JsonType.String; } - } - #endregion - - - #region ICollection Properties - int ICollection.Count { - get { - return Count; - } - } - - bool ICollection.IsSynchronized { - get { - return EnsureCollection ().IsSynchronized; - } - } - - object ICollection.SyncRoot { - get { - return EnsureCollection ().SyncRoot; - } - } - #endregion - - - #region IDictionary Properties - bool IDictionary.IsFixedSize { - get { - return EnsureDictionary ().IsFixedSize; - } - } - - bool IDictionary.IsReadOnly { - get { - return EnsureDictionary ().IsReadOnly; - } - } - - ICollection IDictionary.Keys { - get { - EnsureDictionary (); - IList keys = new List (); - - foreach (KeyValuePair entry in - object_list) { - keys.Add (entry.Key); - } - - return (ICollection) keys; - } - } - - ICollection IDictionary.Values { - get { - EnsureDictionary (); - IList values = new List (); - - foreach (KeyValuePair entry in - object_list) { - values.Add (entry.Value); - } - - return (ICollection) values; - } - } - #endregion - - - - #region IJsonWrapper Properties - bool IJsonWrapper.IsArray { - get { return IsArray; } - } - - bool IJsonWrapper.IsBoolean { - get { return IsBoolean; } - } - - bool IJsonWrapper.IsDouble { - get { return IsDouble; } - } - - bool IJsonWrapper.IsInt { - get { return IsInt; } - } - - bool IJsonWrapper.IsLong { - get { return IsLong; } - } - - bool IJsonWrapper.IsObject { - get { return IsObject; } - } - - bool IJsonWrapper.IsString { - get { return IsString; } - } - #endregion - - - #region IList Properties - bool IList.IsFixedSize { - get { - return EnsureList ().IsFixedSize; - } - } - - bool IList.IsReadOnly { - get { - return EnsureList ().IsReadOnly; - } - } - #endregion - - - #region IDictionary Indexer - object IDictionary.this[object key] { - get { - return EnsureDictionary ()[key]; - } - - set { - if (! (key is String)) - throw new ArgumentException ( - "The key has to be a string"); - - JsonData data = ToJsonData (value); - - this[(string) key] = data; - } - } - #endregion - - - #region IOrderedDictionary Indexer - object IOrderedDictionary.this[int idx] { - get { - EnsureDictionary (); - return object_list[idx].Value; - } - - set { - EnsureDictionary (); - JsonData data = ToJsonData (value); - - KeyValuePair old_entry = object_list[idx]; - - inst_object[old_entry.Key] = data; - - KeyValuePair entry = - new KeyValuePair (old_entry.Key, data); - - object_list[idx] = entry; - } - } - #endregion - - - #region IList Indexer - object IList.this[int index] { - get { - return EnsureList ()[index]; - } - - set { - EnsureList (); - JsonData data = ToJsonData (value); - - this[index] = data; - } - } - #endregion - - - #region Public Indexers - public JsonData this[string prop_name] { - get { - EnsureDictionary (); - return inst_object[prop_name]; - } - - set { - EnsureDictionary (); - - KeyValuePair entry = - new KeyValuePair (prop_name, value); - - if (inst_object.ContainsKey (prop_name)) { - for (int i = 0; i < object_list.Count; i++) { - if (object_list[i].Key == prop_name) { - object_list[i] = entry; - break; - } - } - } else - object_list.Add (entry); - - inst_object[prop_name] = value; - - json = null; - } - } - - public JsonData this[int index] { - get { - EnsureCollection (); - - if (type == JsonType.Array) - return inst_array[index]; - - return object_list[index].Value; - } - - set { - EnsureCollection (); - - if (type == JsonType.Array) - inst_array[index] = value; - else { - KeyValuePair entry = object_list[index]; - KeyValuePair new_entry = - new KeyValuePair (entry.Key, value); - - object_list[index] = new_entry; - inst_object[entry.Key] = value; - } - - json = null; - } - } - #endregion - - - #region Constructors - public JsonData () - { - } - - public JsonData (bool boolean) - { - type = JsonType.Boolean; - inst_boolean = boolean; - } - - public JsonData (double number) - { - type = JsonType.Double; - inst_double = number; - } - - public JsonData (int number) - { - type = JsonType.Int; - inst_int = number; - } - - public JsonData (long number) - { - type = JsonType.Long; - inst_long = number; - } - - public JsonData (object obj) - { - if (obj is Boolean) { - type = JsonType.Boolean; - inst_boolean = (bool) obj; - return; - } - - if (obj is Double) { - type = JsonType.Double; - inst_double = (double) obj; - return; - } - - if (obj is Int32) { - type = JsonType.Int; - inst_int = (int) obj; - return; - } - - if (obj is Int64) { - type = JsonType.Long; - inst_long = (long) obj; - return; - } - - if (obj is String) { - type = JsonType.String; - inst_string = (string) obj; - return; - } - - throw new ArgumentException ( - "Unable to wrap the given object with JsonData"); - } - - public JsonData (string str) - { - type = JsonType.String; - inst_string = str; - } - #endregion - - - #region Implicit Conversions - public static implicit operator JsonData (Boolean data) - { - return new JsonData (data); - } - - public static implicit operator JsonData (Double data) - { - return new JsonData (data); - } - - public static implicit operator JsonData (Int32 data) - { - return new JsonData (data); - } - - public static implicit operator JsonData (Int64 data) - { - return new JsonData (data); - } - - public static implicit operator JsonData (String data) - { - return new JsonData (data); - } - #endregion - - - #region Explicit Conversions - public static explicit operator Boolean (JsonData data) - { - if (data.type != JsonType.Boolean) - throw new InvalidCastException ( - "Instance of JsonData doesn't hold a double"); - - return data.inst_boolean; - } - - public static explicit operator Double (JsonData data) - { - if (data.type != JsonType.Double) - throw new InvalidCastException ( - "Instance of JsonData doesn't hold a double"); - - return data.inst_double; - } - - public static explicit operator Int32 (JsonData data) - { - if (data.type != JsonType.Int) - throw new InvalidCastException ( - "Instance of JsonData doesn't hold an int"); - - return data.inst_int; - } - - public static explicit operator Int64 (JsonData data) - { - if (data.type != JsonType.Long) - throw new InvalidCastException ( - "Instance of JsonData doesn't hold an int"); - - return data.inst_long; - } - - public static explicit operator String (JsonData data) - { - if (data.type != JsonType.String) - throw new InvalidCastException ( - "Instance of JsonData doesn't hold a string"); - - return data.inst_string; - } - #endregion - - - #region ICollection Methods - void ICollection.CopyTo (Array array, int index) - { - EnsureCollection ().CopyTo (array, index); - } - #endregion - - - #region IDictionary Methods - void IDictionary.Add (object key, object value) - { - JsonData data = ToJsonData (value); - - EnsureDictionary ().Add (key, data); - - KeyValuePair entry = - new KeyValuePair ((string) key, data); - object_list.Add (entry); - - json = null; - } - - void IDictionary.Clear () - { - EnsureDictionary ().Clear (); - object_list.Clear (); - json = null; - } - - bool IDictionary.Contains (object key) - { - return EnsureDictionary ().Contains (key); - } - - IDictionaryEnumerator IDictionary.GetEnumerator () - { - return ((IOrderedDictionary) this).GetEnumerator (); - } - - void IDictionary.Remove (object key) - { - EnsureDictionary ().Remove (key); - - for (int i = 0; i < object_list.Count; i++) { - if (object_list[i].Key == (string) key) { - object_list.RemoveAt (i); - break; - } - } - - json = null; - } - #endregion - - - #region IEnumerable Methods - IEnumerator IEnumerable.GetEnumerator () - { - return EnsureCollection ().GetEnumerator (); - } - #endregion - - - #region IJsonWrapper Methods - bool IJsonWrapper.GetBoolean () - { - if (type != JsonType.Boolean) - throw new InvalidOperationException ( - "JsonData instance doesn't hold a boolean"); - - return inst_boolean; - } - - double IJsonWrapper.GetDouble () - { - if (type != JsonType.Double) - throw new InvalidOperationException ( - "JsonData instance doesn't hold a double"); - - return inst_double; - } - - int IJsonWrapper.GetInt () - { - if (type != JsonType.Int) - throw new InvalidOperationException ( - "JsonData instance doesn't hold an int"); - - return inst_int; - } - - long IJsonWrapper.GetLong () - { - if (type != JsonType.Long) - throw new InvalidOperationException ( - "JsonData instance doesn't hold a long"); - - return inst_long; - } - - string IJsonWrapper.GetString () - { - if (type != JsonType.String) - throw new InvalidOperationException ( - "JsonData instance doesn't hold a string"); - - return inst_string; - } - - void IJsonWrapper.SetBoolean (bool val) - { - type = JsonType.Boolean; - inst_boolean = val; - json = null; - } - - void IJsonWrapper.SetDouble (double val) - { - type = JsonType.Double; - inst_double = val; - json = null; - } - - void IJsonWrapper.SetInt (int val) - { - type = JsonType.Int; - inst_int = val; - json = null; - } - - void IJsonWrapper.SetLong (long val) - { - type = JsonType.Long; - inst_long = val; - json = null; - } - - void IJsonWrapper.SetString (string val) - { - type = JsonType.String; - inst_string = val; - json = null; - } - - string IJsonWrapper.ToJson () - { - return ToJson (); - } - - void IJsonWrapper.ToJson (JsonWriter writer) - { - ToJson (writer); - } - #endregion - - - #region IList Methods - int IList.Add (object value) - { - return Add (value); - } - - void IList.Clear () - { - EnsureList ().Clear (); - json = null; - } - - bool IList.Contains (object value) - { - return EnsureList ().Contains (value); - } - - int IList.IndexOf (object value) - { - return EnsureList ().IndexOf (value); - } - - void IList.Insert (int index, object value) - { - EnsureList ().Insert (index, value); - json = null; - } - - void IList.Remove (object value) - { - EnsureList ().Remove (value); - json = null; - } - - void IList.RemoveAt (int index) - { - EnsureList ().RemoveAt (index); - json = null; - } - #endregion - - - #region IOrderedDictionary Methods - IDictionaryEnumerator IOrderedDictionary.GetEnumerator () - { - EnsureDictionary (); - - return new OrderedDictionaryEnumerator ( - object_list.GetEnumerator ()); - } - - void IOrderedDictionary.Insert (int idx, object key, object value) - { - string property = (string) key; - JsonData data = ToJsonData (value); - - this[property] = data; - - KeyValuePair entry = - new KeyValuePair (property, data); - - object_list.Insert (idx, entry); - } - - void IOrderedDictionary.RemoveAt (int idx) - { - EnsureDictionary (); - - inst_object.Remove (object_list[idx].Key); - object_list.RemoveAt (idx); - } - #endregion - - - #region Private Methods - private ICollection EnsureCollection () - { - if (type == JsonType.Array) - return (ICollection) inst_array; - - if (type == JsonType.Object) - return (ICollection) inst_object; - - throw new InvalidOperationException ( - "The JsonData instance has to be initialized first"); - } - - private IDictionary EnsureDictionary () - { - if (type == JsonType.Object) - return (IDictionary) inst_object; - - if (type != JsonType.None) - throw new InvalidOperationException ( - "Instance of JsonData is not a dictionary"); - - type = JsonType.Object; - inst_object = new Dictionary (); - object_list = new List> (); - - return (IDictionary) inst_object; - } - - private IList EnsureList () - { - if (type == JsonType.Array) - return (IList) inst_array; - - if (type != JsonType.None) - throw new InvalidOperationException ( - "Instance of JsonData is not a list"); - - type = JsonType.Array; - inst_array = new List (); - - return (IList) inst_array; - } - - private JsonData ToJsonData (object obj) - { - if (obj == null) - return null; - - if (obj is JsonData) - return (JsonData) obj; - - return new JsonData (obj); - } - - private static void WriteJson (IJsonWrapper obj, JsonWriter writer) - { - if (obj.IsString) { - writer.Write (obj.GetString ()); - return; - } - - if (obj.IsBoolean) { - writer.Write (obj.GetBoolean ()); - return; - } - - if (obj.IsDouble) { - writer.Write (obj.GetDouble ()); - return; - } - - if (obj.IsInt) { - writer.Write (obj.GetInt ()); - return; - } - - if (obj.IsLong) { - writer.Write (obj.GetLong ()); - return; - } - - if (obj.IsArray) { - writer.WriteArrayStart (); - foreach (object elem in (IList) obj) - WriteJson ((JsonData) elem, writer); - writer.WriteArrayEnd (); - - return; - } - - if (obj.IsObject) { - writer.WriteObjectStart (); - - foreach (DictionaryEntry entry in ((IDictionary) obj)) { - writer.WritePropertyName ((string) entry.Key); - WriteJson ((JsonData) entry.Value, writer); - } - writer.WriteObjectEnd (); - - return; - } - } - #endregion - - - public int Add (object value) - { - JsonData data = ToJsonData (value); - - json = null; - - return EnsureList ().Add (data); - } - - public void Clear () - { - if (IsObject) { - ((IDictionary) this).Clear (); - return; - } - - if (IsArray) { - ((IList) this).Clear (); - return; - } - } - - public bool Equals (JsonData x) - { - if (x == null) - return false; - - if (x.type != this.type) - return false; - - switch (this.type) { - case JsonType.None: - return true; - - case JsonType.Object: - return this.inst_object.Equals (x.inst_object); - - case JsonType.Array: - return this.inst_array.Equals (x.inst_array); - - case JsonType.String: - return this.inst_string.Equals (x.inst_string); - - case JsonType.Int: - return this.inst_int.Equals (x.inst_int); - - case JsonType.Long: - return this.inst_long.Equals (x.inst_long); - - case JsonType.Double: - return this.inst_double.Equals (x.inst_double); - - case JsonType.Boolean: - return this.inst_boolean.Equals (x.inst_boolean); - } - - return false; - } - - public JsonType GetJsonType () - { - return type; - } - - public void SetJsonType (JsonType type) - { - if (this.type == type) - return; - - switch (type) { - case JsonType.None: - break; - - case JsonType.Object: - inst_object = new Dictionary (); - object_list = new List> (); - break; - - case JsonType.Array: - inst_array = new List (); - break; - - case JsonType.String: - inst_string = default (String); - break; - - case JsonType.Int: - inst_int = default (Int32); - break; - - case JsonType.Long: - inst_long = default (Int64); - break; - - case JsonType.Double: - inst_double = default (Double); - break; - - case JsonType.Boolean: - inst_boolean = default (Boolean); - break; - } - - this.type = type; - } - - public string ToJson () - { - if (json != null) - return json; - - StringWriter sw = new StringWriter (); - JsonWriter writer = new JsonWriter (sw); - writer.Validate = false; - - WriteJson (this, writer); - json = sw.ToString (); - - return json; - } - - public void ToJson (JsonWriter writer) - { - bool old_validate = writer.Validate; - - writer.Validate = false; - - WriteJson (this, writer); - - writer.Validate = old_validate; - } - - public override string ToString () - { - switch (type) { - case JsonType.Array: - return "JsonData array"; - - case JsonType.Boolean: - return inst_boolean.ToString (); - - case JsonType.Double: - return inst_double.ToString (); - - case JsonType.Int: - return inst_int.ToString (); - - case JsonType.Long: - return inst_long.ToString (); - - case JsonType.Object: - return "JsonData object"; - - case JsonType.String: - return inst_string; - } - - return "Uninitialized JsonData"; - } - } - - - internal class OrderedDictionaryEnumerator : IDictionaryEnumerator - { - IEnumerator> list_enumerator; - - - public object Current { - get { return Entry; } - } - - public DictionaryEntry Entry { - get { - KeyValuePair curr = list_enumerator.Current; - return new DictionaryEntry (curr.Key, curr.Value); - } - } - - public object Key { - get { return list_enumerator.Current.Key; } - } - - public object Value { - get { return list_enumerator.Current.Value; } - } - - - public OrderedDictionaryEnumerator ( - IEnumerator> enumerator) - { - list_enumerator = enumerator; - } - - - public bool MoveNext () - { - return list_enumerator.MoveNext (); - } - - public void Reset () - { - list_enumerator.Reset (); - } - } -} diff --git a/QBox/Json/JsonException.cs b/QBox/Json/JsonException.cs deleted file mode 100644 index f7f8db09..00000000 --- a/QBox/Json/JsonException.cs +++ /dev/null @@ -1,60 +0,0 @@ -#region Header -/** - * JsonException.cs - * Base class throwed by LitJSON when a parsing error occurs. - * - * The authors disclaim copyright to this source code. For more details, see - * the COPYING file included with this distribution. - **/ -#endregion - - -using System; - - -namespace LitJson -{ - public class JsonException : ApplicationException - { - public JsonException () : base () - { - } - - internal JsonException (ParserToken token) : - base (String.Format ( - "Invalid token '{0}' in input string", token)) - { - } - - internal JsonException (ParserToken token, - Exception inner_exception) : - base (String.Format ( - "Invalid token '{0}' in input string", token), - inner_exception) - { - } - - internal JsonException (int c) : - base (String.Format ( - "Invalid character '{0}' in input string", (char) c)) - { - } - - internal JsonException (int c, Exception inner_exception) : - base (String.Format ( - "Invalid character '{0}' in input string", (char) c), - inner_exception) - { - } - - - public JsonException (string message) : base (message) - { - } - - public JsonException (string message, Exception inner_exception) : - base (message, inner_exception) - { - } - } -} diff --git a/QBox/Json/JsonMapper.cs b/QBox/Json/JsonMapper.cs deleted file mode 100644 index f82646ca..00000000 --- a/QBox/Json/JsonMapper.cs +++ /dev/null @@ -1,911 +0,0 @@ -#region Header -/** - * JsonMapper.cs - * JSON to .Net object and object to JSON conversions. - * - * The authors disclaim copyright to this source code. For more details, see - * the COPYING file included with this distribution. - **/ -#endregion - - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Reflection; - - -namespace LitJson -{ - internal struct PropertyMetadata - { - public MemberInfo Info; - public bool IsField; - public Type Type; - } - - - internal struct ArrayMetadata - { - private Type element_type; - private bool is_array; - private bool is_list; - - - public Type ElementType { - get { - if (element_type == null) - return typeof (JsonData); - - return element_type; - } - - set { element_type = value; } - } - - public bool IsArray { - get { return is_array; } - set { is_array = value; } - } - - public bool IsList { - get { return is_list; } - set { is_list = value; } - } - } - - - internal struct ObjectMetadata - { - private Type element_type; - private bool is_dictionary; - - private IDictionary properties; - - - public Type ElementType { - get { - if (element_type == null) - return typeof (JsonData); - - return element_type; - } - - set { element_type = value; } - } - - public bool IsDictionary { - get { return is_dictionary; } - set { is_dictionary = value; } - } - - public IDictionary Properties { - get { return properties; } - set { properties = value; } - } - } - - - internal delegate void ExporterFunc (object obj, JsonWriter writer); - public delegate void ExporterFunc (T obj, JsonWriter writer); - - internal delegate object ImporterFunc (object input); - public delegate TValue ImporterFunc (TJson input); - - public delegate IJsonWrapper WrapperFactory (); - - - public class JsonMapper - { - #region Fields - private static int max_nesting_depth; - - private static IFormatProvider datetime_format; - - private static IDictionary base_exporters_table; - private static IDictionary custom_exporters_table; - - private static IDictionary> base_importers_table; - private static IDictionary> custom_importers_table; - - private static IDictionary array_metadata; - private static readonly object array_metadata_lock = new Object (); - - private static IDictionary> conv_ops; - private static readonly object conv_ops_lock = new Object (); - - private static IDictionary object_metadata; - private static readonly object object_metadata_lock = new Object (); - - private static IDictionary> type_properties; - private static readonly object type_properties_lock = new Object (); - - private static JsonWriter static_writer; - private static readonly object static_writer_lock = new Object (); - #endregion - - - #region Constructors - static JsonMapper () - { - max_nesting_depth = 100; - - array_metadata = new Dictionary (); - conv_ops = new Dictionary> (); - object_metadata = new Dictionary (); - type_properties = new Dictionary> (); - - static_writer = new JsonWriter (); - - datetime_format = DateTimeFormatInfo.InvariantInfo; - - base_exporters_table = new Dictionary (); - custom_exporters_table = new Dictionary (); - - base_importers_table = new Dictionary> (); - custom_importers_table = new Dictionary> (); - - RegisterBaseExporters (); - RegisterBaseImporters (); - } - #endregion - - - #region Private Methods - private static void AddArrayMetadata (Type type) - { - if (array_metadata.ContainsKey (type)) - return; - - ArrayMetadata data = new ArrayMetadata (); - - data.IsArray = type.IsArray; - - if (type.GetInterface ("System.Collections.IList") != null) - data.IsList = true; - - foreach (PropertyInfo p_info in type.GetProperties ()) { - if (p_info.Name != "Item") - continue; - - ParameterInfo[] parameters = p_info.GetIndexParameters (); - - if (parameters.Length != 1) - continue; - - if (parameters[0].ParameterType == typeof (int)) - data.ElementType = p_info.PropertyType; - } - - lock (array_metadata_lock) { - try { - array_metadata.Add (type, data); - } catch (ArgumentException) { - return; - } - } - } - - private static void AddObjectMetadata (Type type) - { - if (object_metadata.ContainsKey (type)) - return; - - ObjectMetadata data = new ObjectMetadata (); - - if (type.GetInterface ("System.Collections.IDictionary") != null) - data.IsDictionary = true; - - data.Properties = new Dictionary (); - - foreach (PropertyInfo p_info in type.GetProperties ()) { - if (p_info.Name == "Item") { - ParameterInfo[] parameters = p_info.GetIndexParameters (); - - if (parameters.Length != 1) - continue; - - if (parameters[0].ParameterType == typeof (string)) - data.ElementType = p_info.PropertyType; - - continue; - } - - PropertyMetadata p_data = new PropertyMetadata (); - p_data.Info = p_info; - p_data.Type = p_info.PropertyType; - - data.Properties.Add (p_info.Name, p_data); - } - - foreach (FieldInfo f_info in type.GetFields ()) { - PropertyMetadata p_data = new PropertyMetadata (); - p_data.Info = f_info; - p_data.IsField = true; - p_data.Type = f_info.FieldType; - - data.Properties.Add (f_info.Name, p_data); - } - - lock (object_metadata_lock) { - try { - object_metadata.Add (type, data); - } catch (ArgumentException) { - return; - } - } - } - - private static void AddTypeProperties (Type type) - { - if (type_properties.ContainsKey (type)) - return; - - IList props = new List (); - - foreach (PropertyInfo p_info in type.GetProperties ()) { - if (p_info.Name == "Item") - continue; - - PropertyMetadata p_data = new PropertyMetadata (); - p_data.Info = p_info; - p_data.IsField = false; - props.Add (p_data); - } - - foreach (FieldInfo f_info in type.GetFields ()) { - PropertyMetadata p_data = new PropertyMetadata (); - p_data.Info = f_info; - p_data.IsField = true; - - props.Add (p_data); - } - - lock (type_properties_lock) { - try { - type_properties.Add (type, props); - } catch (ArgumentException) { - return; - } - } - } - - private static MethodInfo GetConvOp (Type t1, Type t2) - { - lock (conv_ops_lock) { - if (! conv_ops.ContainsKey (t1)) - conv_ops.Add (t1, new Dictionary ()); - } - - if (conv_ops[t1].ContainsKey (t2)) - return conv_ops[t1][t2]; - - MethodInfo op = t1.GetMethod ( - "op_Implicit", new Type[] { t2 }); - - lock (conv_ops_lock) { - try { - conv_ops[t1].Add (t2, op); - } catch (ArgumentException) { - return conv_ops[t1][t2]; - } - } - - return op; - } - - private static object ReadValue (Type inst_type, JsonReader reader) - { - reader.Read (); - - if (reader.Token == JsonToken.ArrayEnd) - return null; - - if (reader.Token == JsonToken.Null) { - - if (! inst_type.IsClass) - throw new JsonException (String.Format ( - "Can't assign null to an instance of type {0}", - inst_type)); - - return null; - } - - if (reader.Token == JsonToken.Double || - reader.Token == JsonToken.Int || - reader.Token == JsonToken.Long || - reader.Token == JsonToken.String || - reader.Token == JsonToken.Boolean) { - - Type json_type = reader.Value.GetType (); - - if (inst_type.IsAssignableFrom (json_type)) - return reader.Value; - - // If there's a custom importer that fits, use it - if (custom_importers_table.ContainsKey (json_type) && - custom_importers_table[json_type].ContainsKey ( - inst_type)) { - - ImporterFunc importer = - custom_importers_table[json_type][inst_type]; - - return importer (reader.Value); - } - - // Maybe there's a base importer that works - if (base_importers_table.ContainsKey (json_type) && - base_importers_table[json_type].ContainsKey ( - inst_type)) { - - ImporterFunc importer = - base_importers_table[json_type][inst_type]; - - return importer (reader.Value); - } - - // Maybe it's an enum - if (inst_type.IsEnum) - return Enum.ToObject (inst_type, reader.Value); - - // Try using an implicit conversion operator - MethodInfo conv_op = GetConvOp (inst_type, json_type); - - if (conv_op != null) - return conv_op.Invoke (null, - new object[] { reader.Value }); - - // No luck - throw new JsonException (String.Format ( - "Can't assign value '{0}' (type {1}) to type {2}", - reader.Value, json_type, inst_type)); - } - - object instance = null; - - if (reader.Token == JsonToken.ArrayStart) { - - AddArrayMetadata (inst_type); - ArrayMetadata t_data = array_metadata[inst_type]; - - if (! t_data.IsArray && ! t_data.IsList) - throw new JsonException (String.Format ( - "Type {0} can't act as an array", - inst_type)); - - IList list; - Type elem_type; - - if (! t_data.IsArray) { - list = (IList) Activator.CreateInstance (inst_type); - elem_type = t_data.ElementType; - } else { - list = new ArrayList (); - elem_type = inst_type.GetElementType (); - } - - while (true) { - object item = ReadValue (elem_type, reader); - if (reader.Token == JsonToken.ArrayEnd) - break; - - list.Add (item); - } - - if (t_data.IsArray) { - int n = list.Count; - instance = Array.CreateInstance (elem_type, n); - - for (int i = 0; i < n; i++) - ((Array) instance).SetValue (list[i], i); - } else - instance = list; - - } else if (reader.Token == JsonToken.ObjectStart) { - - AddObjectMetadata (inst_type); - ObjectMetadata t_data = object_metadata[inst_type]; - - instance = Activator.CreateInstance (inst_type); - - while (true) { - reader.Read (); - - if (reader.Token == JsonToken.ObjectEnd) - break; - - string property = (string) reader.Value; - - if (t_data.Properties.ContainsKey (property)) { - PropertyMetadata prop_data = - t_data.Properties[property]; - - if (prop_data.IsField) { - ((FieldInfo) prop_data.Info).SetValue ( - instance, ReadValue (prop_data.Type, reader)); - } else { - PropertyInfo p_info = - (PropertyInfo) prop_data.Info; - - if (p_info.CanWrite) - p_info.SetValue ( - instance, - ReadValue (prop_data.Type, reader), - null); - else - ReadValue (prop_data.Type, reader); - } - - } else { - if (! t_data.IsDictionary) - throw new JsonException (String.Format ( - "The type {0} doesn't have the " + - "property '{1}'", inst_type, property)); - - ((IDictionary) instance).Add ( - property, ReadValue ( - t_data.ElementType, reader)); - } - - } - - } - - return instance; - } - - private static IJsonWrapper ReadValue (WrapperFactory factory, - JsonReader reader) - { - reader.Read (); - - if (reader.Token == JsonToken.ArrayEnd || - reader.Token == JsonToken.Null) - return null; - - IJsonWrapper instance = factory (); - - if (reader.Token == JsonToken.String) { - instance.SetString ((string) reader.Value); - return instance; - } - - if (reader.Token == JsonToken.Double) { - instance.SetDouble ((double) reader.Value); - return instance; - } - - if (reader.Token == JsonToken.Int) { - instance.SetInt ((int) reader.Value); - return instance; - } - - if (reader.Token == JsonToken.Long) { - instance.SetLong ((long) reader.Value); - return instance; - } - - if (reader.Token == JsonToken.Boolean) { - instance.SetBoolean ((bool) reader.Value); - return instance; - } - - if (reader.Token == JsonToken.ArrayStart) { - instance.SetJsonType (JsonType.Array); - - while (true) { - IJsonWrapper item = ReadValue (factory, reader); - if (reader.Token == JsonToken.ArrayEnd) - break; - - ((IList) instance).Add (item); - } - } - else if (reader.Token == JsonToken.ObjectStart) { - instance.SetJsonType (JsonType.Object); - - while (true) { - reader.Read (); - - if (reader.Token == JsonToken.ObjectEnd) - break; - - string property = (string) reader.Value; - - ((IDictionary) instance)[property] = ReadValue ( - factory, reader); - } - - } - - return instance; - } - - private static void RegisterBaseExporters () - { - base_exporters_table[typeof (byte)] = - delegate (object obj, JsonWriter writer) { - writer.Write (Convert.ToInt32 ((byte) obj)); - }; - - base_exporters_table[typeof (char)] = - delegate (object obj, JsonWriter writer) { - writer.Write (Convert.ToString ((char) obj)); - }; - - base_exporters_table[typeof (DateTime)] = - delegate (object obj, JsonWriter writer) { - writer.Write (Convert.ToString ((DateTime) obj, - datetime_format)); - }; - - base_exporters_table[typeof (decimal)] = - delegate (object obj, JsonWriter writer) { - writer.Write ((decimal) obj); - }; - - base_exporters_table[typeof (sbyte)] = - delegate (object obj, JsonWriter writer) { - writer.Write (Convert.ToInt32 ((sbyte) obj)); - }; - - base_exporters_table[typeof (short)] = - delegate (object obj, JsonWriter writer) { - writer.Write (Convert.ToInt32 ((short) obj)); - }; - - base_exporters_table[typeof (ushort)] = - delegate (object obj, JsonWriter writer) { - writer.Write (Convert.ToInt32 ((ushort) obj)); - }; - - base_exporters_table[typeof (uint)] = - delegate (object obj, JsonWriter writer) { - writer.Write (Convert.ToUInt64 ((uint) obj)); - }; - - base_exporters_table[typeof (ulong)] = - delegate (object obj, JsonWriter writer) { - writer.Write ((ulong) obj); - }; - } - - private static void RegisterBaseImporters () - { - ImporterFunc importer; - - importer = delegate (object input) { - return Convert.ToByte ((int) input); - }; - RegisterImporter (base_importers_table, typeof (int), - typeof (byte), importer); - - importer = delegate (object input) { - return Convert.ToUInt64 ((int) input); - }; - RegisterImporter (base_importers_table, typeof (int), - typeof (ulong), importer); - - importer = delegate (object input) { - return Convert.ToSByte ((int) input); - }; - RegisterImporter (base_importers_table, typeof (int), - typeof (sbyte), importer); - - importer = delegate (object input) { - return Convert.ToInt16 ((int) input); - }; - RegisterImporter (base_importers_table, typeof (int), - typeof (short), importer); - - importer = delegate (object input) { - return Convert.ToUInt16 ((int) input); - }; - RegisterImporter (base_importers_table, typeof (int), - typeof (ushort), importer); - - importer = delegate (object input) { - return Convert.ToUInt32 ((int) input); - }; - RegisterImporter (base_importers_table, typeof (int), - typeof (uint), importer); - - importer = delegate (object input) { - return Convert.ToSingle ((int) input); - }; - RegisterImporter (base_importers_table, typeof (int), - typeof (float), importer); - - importer = delegate (object input) { - return Convert.ToDouble ((int) input); - }; - RegisterImporter (base_importers_table, typeof (int), - typeof (double), importer); - - importer = delegate (object input) { - return Convert.ToDecimal ((double) input); - }; - RegisterImporter (base_importers_table, typeof (double), - typeof (decimal), importer); - - - importer = delegate (object input) { - return Convert.ToUInt32 ((long) input); - }; - RegisterImporter (base_importers_table, typeof (long), - typeof (uint), importer); - - importer = delegate (object input) { - return Convert.ToChar ((string) input); - }; - RegisterImporter (base_importers_table, typeof (string), - typeof (char), importer); - - importer = delegate (object input) { - return Convert.ToDateTime ((string) input, datetime_format); - }; - RegisterImporter (base_importers_table, typeof (string), - typeof (DateTime), importer); - } - - private static void RegisterImporter ( - IDictionary> table, - Type json_type, Type value_type, ImporterFunc importer) - { - if (! table.ContainsKey (json_type)) - table.Add (json_type, new Dictionary ()); - - table[json_type][value_type] = importer; - } - - private static void WriteValue (object obj, JsonWriter writer, - bool writer_is_private, - int depth) - { - if (depth > max_nesting_depth) - throw new JsonException ( - String.Format ("Max allowed object depth reached while " + - "trying to export from type {0}", - obj.GetType ())); - - if (obj == null) { - writer.Write (null); - return; - } - - if (obj is IJsonWrapper) { - if (writer_is_private) - writer.TextWriter.Write (((IJsonWrapper) obj).ToJson ()); - else - ((IJsonWrapper) obj).ToJson (writer); - - return; - } - - if (obj is String) { - writer.Write ((string) obj); - return; - } - - if (obj is Double) { - writer.Write ((double) obj); - return; - } - - if (obj is Int32) { - writer.Write ((int) obj); - return; - } - - if (obj is Boolean) { - writer.Write ((bool) obj); - return; - } - - if (obj is Int64) { - writer.Write ((long) obj); - return; - } - - if (obj is Array) { - writer.WriteArrayStart (); - - foreach (object elem in (Array) obj) - WriteValue (elem, writer, writer_is_private, depth + 1); - - writer.WriteArrayEnd (); - - return; - } - - if (obj is IList) { - writer.WriteArrayStart (); - foreach (object elem in (IList) obj) - WriteValue (elem, writer, writer_is_private, depth + 1); - writer.WriteArrayEnd (); - - return; - } - - if (obj is IDictionary) { - writer.WriteObjectStart (); - foreach (DictionaryEntry entry in (IDictionary) obj) { - writer.WritePropertyName ((string) entry.Key); - WriteValue (entry.Value, writer, writer_is_private, - depth + 1); - } - writer.WriteObjectEnd (); - - return; - } - - Type obj_type = obj.GetType (); - - // See if there's a custom exporter for the object - if (custom_exporters_table.ContainsKey (obj_type)) { - ExporterFunc exporter = custom_exporters_table[obj_type]; - exporter (obj, writer); - - return; - } - - // If not, maybe there's a base exporter - if (base_exporters_table.ContainsKey (obj_type)) { - ExporterFunc exporter = base_exporters_table[obj_type]; - exporter (obj, writer); - - return; - } - - // Last option, let's see if it's an enum - if (obj is Enum) { - Type e_type = Enum.GetUnderlyingType (obj_type); - - if (e_type == typeof (long) - || e_type == typeof (uint) - || e_type == typeof (ulong)) - writer.Write ((ulong) obj); - else - writer.Write ((int) obj); - - return; - } - - // Okay, so it looks like the input should be exported as an - // object - AddTypeProperties (obj_type); - IList props = type_properties[obj_type]; - - writer.WriteObjectStart (); - foreach (PropertyMetadata p_data in props) { - if (p_data.IsField) { - writer.WritePropertyName (p_data.Info.Name); - WriteValue (((FieldInfo) p_data.Info).GetValue (obj), - writer, writer_is_private, depth + 1); - } - else { - PropertyInfo p_info = (PropertyInfo) p_data.Info; - - if (p_info.CanRead) { - writer.WritePropertyName (p_data.Info.Name); - WriteValue (p_info.GetValue (obj, null), - writer, writer_is_private, depth + 1); - } - } - } - writer.WriteObjectEnd (); - } - #endregion - - - public static string ToJson (object obj) - { - lock (static_writer_lock) { - static_writer.Reset (); - - WriteValue (obj, static_writer, true, 0); - - return static_writer.ToString (); - } - } - - public static void ToJson (object obj, JsonWriter writer) - { - WriteValue (obj, writer, false, 0); - } - - public static JsonData ToObject (JsonReader reader) - { - return (JsonData) ToWrapper ( - delegate { return new JsonData (); }, reader); - } - - public static JsonData ToObject (TextReader reader) - { - JsonReader json_reader = new JsonReader (reader); - - return (JsonData) ToWrapper ( - delegate { return new JsonData (); }, json_reader); - } - - public static JsonData ToObject (string json) - { - return (JsonData) ToWrapper ( - delegate { return new JsonData (); }, json); - } - - public static T ToObject (JsonReader reader) - { - return (T) ReadValue (typeof (T), reader); - } - - public static T ToObject (TextReader reader) - { - JsonReader json_reader = new JsonReader (reader); - - return (T) ReadValue (typeof (T), json_reader); - } - - public static T ToObject (string json) - { - JsonReader reader = new JsonReader (json); - - return (T) ReadValue (typeof (T), reader); - } - - public static IJsonWrapper ToWrapper (WrapperFactory factory, - JsonReader reader) - { - return ReadValue (factory, reader); - } - - public static IJsonWrapper ToWrapper (WrapperFactory factory, - string json) - { - JsonReader reader = new JsonReader (json); - - return ReadValue (factory, reader); - } - - public static void RegisterExporter (ExporterFunc exporter) - { - ExporterFunc exporter_wrapper = - delegate (object obj, JsonWriter writer) { - exporter ((T) obj, writer); - }; - - custom_exporters_table[typeof (T)] = exporter_wrapper; - } - - public static void RegisterImporter ( - ImporterFunc importer) - { - ImporterFunc importer_wrapper = - delegate (object input) { - return importer ((TJson) input); - }; - - RegisterImporter (custom_importers_table, typeof (TJson), - typeof (TValue), importer_wrapper); - } - - public static void UnregisterExporters () - { - custom_exporters_table.Clear (); - } - - public static void UnregisterImporters () - { - custom_importers_table.Clear (); - } - } -} diff --git a/QBox/Json/JsonReader.cs b/QBox/Json/JsonReader.cs deleted file mode 100644 index 4d12475c..00000000 --- a/QBox/Json/JsonReader.cs +++ /dev/null @@ -1,455 +0,0 @@ -#region Header -/** - * JsonReader.cs - * Stream-like access to JSON text. - * - * The authors disclaim copyright to this source code. For more details, see - * the COPYING file included with this distribution. - **/ -#endregion - - -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; - - -namespace LitJson -{ - public enum JsonToken - { - None, - - ObjectStart, - PropertyName, - ObjectEnd, - - ArrayStart, - ArrayEnd, - - Int, - Long, - Double, - - String, - - Boolean, - Null - } - - - public class JsonReader - { - #region Fields - private static IDictionary> parse_table; - - private Stack automaton_stack; - private int current_input; - private int current_symbol; - private bool end_of_json; - private bool end_of_input; - private Lexer lexer; - private bool parser_in_string; - private bool parser_return; - private bool read_started; - private TextReader reader; - private bool reader_is_owned; - private object token_value; - private JsonToken token; - #endregion - - - #region Public Properties - public bool AllowComments { - get { return lexer.AllowComments; } - set { lexer.AllowComments = value; } - } - - public bool AllowSingleQuotedStrings { - get { return lexer.AllowSingleQuotedStrings; } - set { lexer.AllowSingleQuotedStrings = value; } - } - - public bool EndOfInput { - get { return end_of_input; } - } - - public bool EndOfJson { - get { return end_of_json; } - } - - public JsonToken Token { - get { return token; } - } - - public object Value { - get { return token_value; } - } - #endregion - - - #region Constructors - static JsonReader () - { - PopulateParseTable (); - } - - public JsonReader (string json_text) : - this (new StringReader (json_text), true) - { - } - - public JsonReader (TextReader reader) : - this (reader, false) - { - } - - private JsonReader (TextReader reader, bool owned) - { - if (reader == null) - throw new ArgumentNullException ("reader"); - - parser_in_string = false; - parser_return = false; - - read_started = false; - automaton_stack = new Stack (); - automaton_stack.Push ((int) ParserToken.End); - automaton_stack.Push ((int) ParserToken.Text); - - lexer = new Lexer (reader); - - end_of_input = false; - end_of_json = false; - - this.reader = reader; - reader_is_owned = owned; - } - #endregion - - - #region Static Methods - private static void PopulateParseTable () - { - parse_table = new Dictionary> (); - - TableAddRow (ParserToken.Array); - TableAddCol (ParserToken.Array, '[', - '[', - (int) ParserToken.ArrayPrime); - - TableAddRow (ParserToken.ArrayPrime); - TableAddCol (ParserToken.ArrayPrime, '"', - (int) ParserToken.Value, - - (int) ParserToken.ValueRest, - ']'); - TableAddCol (ParserToken.ArrayPrime, '[', - (int) ParserToken.Value, - (int) ParserToken.ValueRest, - ']'); - TableAddCol (ParserToken.ArrayPrime, ']', - ']'); - TableAddCol (ParserToken.ArrayPrime, '{', - (int) ParserToken.Value, - (int) ParserToken.ValueRest, - ']'); - TableAddCol (ParserToken.ArrayPrime, (int) ParserToken.Number, - (int) ParserToken.Value, - (int) ParserToken.ValueRest, - ']'); - TableAddCol (ParserToken.ArrayPrime, (int) ParserToken.True, - (int) ParserToken.Value, - (int) ParserToken.ValueRest, - ']'); - TableAddCol (ParserToken.ArrayPrime, (int) ParserToken.False, - (int) ParserToken.Value, - (int) ParserToken.ValueRest, - ']'); - TableAddCol (ParserToken.ArrayPrime, (int) ParserToken.Null, - (int) ParserToken.Value, - (int) ParserToken.ValueRest, - ']'); - - TableAddRow (ParserToken.Object); - TableAddCol (ParserToken.Object, '{', - '{', - (int) ParserToken.ObjectPrime); - - TableAddRow (ParserToken.ObjectPrime); - TableAddCol (ParserToken.ObjectPrime, '"', - (int) ParserToken.Pair, - (int) ParserToken.PairRest, - '}'); - TableAddCol (ParserToken.ObjectPrime, '}', - '}'); - - TableAddRow (ParserToken.Pair); - TableAddCol (ParserToken.Pair, '"', - (int) ParserToken.String, - ':', - (int) ParserToken.Value); - - TableAddRow (ParserToken.PairRest); - TableAddCol (ParserToken.PairRest, ',', - ',', - (int) ParserToken.Pair, - (int) ParserToken.PairRest); - TableAddCol (ParserToken.PairRest, '}', - (int) ParserToken.Epsilon); - - TableAddRow (ParserToken.String); - TableAddCol (ParserToken.String, '"', - '"', - (int) ParserToken.CharSeq, - '"'); - - TableAddRow (ParserToken.Text); - TableAddCol (ParserToken.Text, '[', - (int) ParserToken.Array); - TableAddCol (ParserToken.Text, '{', - (int) ParserToken.Object); - - TableAddRow (ParserToken.Value); - TableAddCol (ParserToken.Value, '"', - (int) ParserToken.String); - TableAddCol (ParserToken.Value, '[', - (int) ParserToken.Array); - TableAddCol (ParserToken.Value, '{', - (int) ParserToken.Object); - TableAddCol (ParserToken.Value, (int) ParserToken.Number, - (int) ParserToken.Number); - TableAddCol (ParserToken.Value, (int) ParserToken.True, - (int) ParserToken.True); - TableAddCol (ParserToken.Value, (int) ParserToken.False, - (int) ParserToken.False); - TableAddCol (ParserToken.Value, (int) ParserToken.Null, - (int) ParserToken.Null); - - TableAddRow (ParserToken.ValueRest); - TableAddCol (ParserToken.ValueRest, ',', - ',', - (int) ParserToken.Value, - (int) ParserToken.ValueRest); - TableAddCol (ParserToken.ValueRest, ']', - (int) ParserToken.Epsilon); - } - - private static void TableAddCol (ParserToken row, int col, - params int[] symbols) - { - parse_table[(int) row].Add (col, symbols); - } - - private static void TableAddRow (ParserToken rule) - { - parse_table.Add ((int) rule, new Dictionary ()); - } - #endregion - - - #region Private Methods - private void ProcessNumber (string number) - { - if (number.IndexOf ('.') != -1 || - number.IndexOf ('e') != -1 || - number.IndexOf ('E') != -1) { - - double n_double; - if (Double.TryParse (number, out n_double)) { - token = JsonToken.Double; - token_value = n_double; - - return; - } - } - - int n_int32; - if (Int32.TryParse (number, out n_int32)) { - token = JsonToken.Int; - token_value = n_int32; - - return; - } - - long n_int64; - if (Int64.TryParse (number, out n_int64)) { - token = JsonToken.Long; - token_value = n_int64; - - return; - } - - // Shouldn't happen, but just in case, return something - token = JsonToken.Int; - token_value = 0; - } - - private void ProcessSymbol () - { - if (current_symbol == '[') { - token = JsonToken.ArrayStart; - parser_return = true; - - } else if (current_symbol == ']') { - token = JsonToken.ArrayEnd; - parser_return = true; - - } else if (current_symbol == '{') { - token = JsonToken.ObjectStart; - parser_return = true; - - } else if (current_symbol == '}') { - token = JsonToken.ObjectEnd; - parser_return = true; - - } else if (current_symbol == '"') { - if (parser_in_string) { - parser_in_string = false; - - parser_return = true; - - } else { - if (token == JsonToken.None) - token = JsonToken.String; - - parser_in_string = true; - } - - } else if (current_symbol == (int) ParserToken.CharSeq) { - token_value = lexer.StringValue; - - } else if (current_symbol == (int) ParserToken.False) { - token = JsonToken.Boolean; - token_value = false; - parser_return = true; - - } else if (current_symbol == (int) ParserToken.Null) { - token = JsonToken.Null; - parser_return = true; - - } else if (current_symbol == (int) ParserToken.Number) { - ProcessNumber (lexer.StringValue); - - parser_return = true; - - } else if (current_symbol == (int) ParserToken.Pair) { - token = JsonToken.PropertyName; - - } else if (current_symbol == (int) ParserToken.True) { - token = JsonToken.Boolean; - token_value = true; - parser_return = true; - - } - } - - private bool ReadToken () - { - if (end_of_input) - return false; - - lexer.NextToken (); - - if (lexer.EndOfInput) { - Close (); - - return false; - } - - current_input = lexer.Token; - - return true; - } - #endregion - - - public void Close () - { - if (end_of_input) - return; - - end_of_input = true; - end_of_json = true; - - if (reader_is_owned) - reader.Close (); - - reader = null; - } - - public bool Read () - { - if (end_of_input) - return false; - - if (end_of_json) { - end_of_json = false; - automaton_stack.Clear (); - automaton_stack.Push ((int) ParserToken.End); - automaton_stack.Push ((int) ParserToken.Text); - } - - parser_in_string = false; - parser_return = false; - - token = JsonToken.None; - token_value = null; - - if (! read_started) { - read_started = true; - - if (! ReadToken ()) - return false; - } - - - int[] entry_symbols; - - while (true) { - if (parser_return) { - if (automaton_stack.Peek () == (int) ParserToken.End) - end_of_json = true; - - return true; - } - - current_symbol = automaton_stack.Pop (); - - ProcessSymbol (); - - if (current_symbol == current_input) { - if (! ReadToken ()) { - if (automaton_stack.Peek () != (int) ParserToken.End) - throw new JsonException ( - "Input doesn't evaluate to proper JSON text"); - - if (parser_return) - return true; - - return false; - } - - continue; - } - - try { - - entry_symbols = - parse_table[current_symbol][current_input]; - - } catch (KeyNotFoundException e) { - throw new JsonException ((ParserToken) current_input, e); - } - - if (entry_symbols[0] == (int) ParserToken.Epsilon) - continue; - - for (int i = entry_symbols.Length - 1; i >= 0; i--) - automaton_stack.Push (entry_symbols[i]); - } - } - - } -} diff --git a/QBox/Json/JsonWriter.cs b/QBox/Json/JsonWriter.cs deleted file mode 100644 index 0d910d42..00000000 --- a/QBox/Json/JsonWriter.cs +++ /dev/null @@ -1,462 +0,0 @@ -#region Header -/** - * JsonWriter.cs - * Stream-like facility to output JSON text. - * - * The authors disclaim copyright to this source code. For more details, see - * the COPYING file included with this distribution. - **/ -#endregion - - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Text; - - -namespace LitJson -{ - internal enum Condition - { - InArray, - InObject, - NotAProperty, - Property, - Value - } - - internal class WriterContext - { - public int Count; - public bool InArray; - public bool InObject; - public bool ExpectingValue; - public int Padding; - } - - public class JsonWriter - { - #region Fields - private static NumberFormatInfo number_format; - - private WriterContext context; - private Stack ctx_stack; - private bool has_reached_end; - private char[] hex_seq; - private int indentation; - private int indent_value; - private StringBuilder inst_string_builder; - private bool pretty_print; - private bool validate; - private TextWriter writer; - #endregion - - - #region Properties - public int IndentValue { - get { return indent_value; } - set { - indentation = (indentation / indent_value) * value; - indent_value = value; - } - } - - public bool PrettyPrint { - get { return pretty_print; } - set { pretty_print = value; } - } - - public TextWriter TextWriter { - get { return writer; } - } - - public bool Validate { - get { return validate; } - set { validate = value; } - } - #endregion - - - #region Constructors - static JsonWriter () - { - number_format = NumberFormatInfo.InvariantInfo; - } - - public JsonWriter () - { - inst_string_builder = new StringBuilder (); - writer = new StringWriter (inst_string_builder); - - Init (); - } - - public JsonWriter (StringBuilder sb) : - this (new StringWriter (sb)) - { - } - - public JsonWriter (TextWriter writer) - { - if (writer == null) - throw new ArgumentNullException ("writer"); - - this.writer = writer; - - Init (); - } - #endregion - - - #region Private Methods - private void DoValidation (Condition cond) - { - if (! context.ExpectingValue) - context.Count++; - - if (! validate) - return; - - if (has_reached_end) - throw new JsonException ( - "A complete JSON symbol has already been written"); - - switch (cond) { - case Condition.InArray: - if (! context.InArray) - throw new JsonException ( - "Can't close an array here"); - break; - - case Condition.InObject: - if (! context.InObject || context.ExpectingValue) - throw new JsonException ( - "Can't close an object here"); - break; - - case Condition.NotAProperty: - if (context.InObject && ! context.ExpectingValue) - throw new JsonException ( - "Expected a property"); - break; - - case Condition.Property: - if (! context.InObject || context.ExpectingValue) - throw new JsonException ( - "Can't add a property here"); - break; - - case Condition.Value: - if (! context.InArray && - (! context.InObject || ! context.ExpectingValue)) - throw new JsonException ( - "Can't add a value here"); - - break; - } - } - - private void Init () - { - has_reached_end = false; - hex_seq = new char[4]; - indentation = 0; - indent_value = 4; - pretty_print = false; - validate = true; - - ctx_stack = new Stack (); - context = new WriterContext (); - ctx_stack.Push (context); - } - - private static void IntToHex (int n, char[] hex) - { - int num; - - for (int i = 0; i < 4; i++) { - num = n % 16; - - if (num < 10) - hex[3 - i] = (char) ('0' + num); - else - hex[3 - i] = (char) ('A' + (num - 10)); - - n >>= 4; - } - } - - private void Indent () - { - if (pretty_print) - indentation += indent_value; - } - - - private void Put (string str) - { - if (pretty_print && ! context.ExpectingValue) - for (int i = 0; i < indentation; i++) - writer.Write (' '); - - writer.Write (str); - } - - private void PutNewline () - { - PutNewline (true); - } - - private void PutNewline (bool add_comma) - { - if (add_comma && ! context.ExpectingValue && - context.Count > 1) - writer.Write (','); - - if (pretty_print && ! context.ExpectingValue) - writer.Write ('\n'); - } - - private void PutString (string str) - { - Put (String.Empty); - - writer.Write ('"'); - - int n = str.Length; - for (int i = 0; i < n; i++) { - switch (str[i]) { - case '\n': - writer.Write ("\\n"); - continue; - - case '\r': - writer.Write ("\\r"); - continue; - - case '\t': - writer.Write ("\\t"); - continue; - - case '"': - case '\\': - writer.Write ('\\'); - writer.Write (str[i]); - continue; - - case '\f': - writer.Write ("\\f"); - continue; - - case '\b': - writer.Write ("\\b"); - continue; - } - - if ((int) str[i] >= 32 && (int) str[i] <= 126) { - writer.Write (str[i]); - continue; - } - - // Default, turn into a \uXXXX sequence - IntToHex ((int) str[i], hex_seq); - writer.Write ("\\u"); - writer.Write (hex_seq); - } - - writer.Write ('"'); - } - - private void Unindent () - { - if (pretty_print) - indentation -= indent_value; - } - #endregion - - - public override string ToString () - { - if (inst_string_builder == null) - return String.Empty; - - return inst_string_builder.ToString (); - } - - public void Reset () - { - has_reached_end = false; - - ctx_stack.Clear (); - context = new WriterContext (); - ctx_stack.Push (context); - - if (inst_string_builder != null) - inst_string_builder.Remove (0, inst_string_builder.Length); - } - - public void Write (bool boolean) - { - DoValidation (Condition.Value); - PutNewline (); - - Put (boolean ? "true" : "false"); - - context.ExpectingValue = false; - } - - public void Write (decimal number) - { - DoValidation (Condition.Value); - PutNewline (); - - Put (Convert.ToString (number, number_format)); - - context.ExpectingValue = false; - } - - public void Write (double number) - { - DoValidation (Condition.Value); - PutNewline (); - - string str = Convert.ToString (number, number_format); - Put (str); - - if (str.IndexOf ('.') == -1 && - str.IndexOf ('E') == -1) - writer.Write (".0"); - - context.ExpectingValue = false; - } - - public void Write (int number) - { - DoValidation (Condition.Value); - PutNewline (); - - Put (Convert.ToString (number, number_format)); - - context.ExpectingValue = false; - } - - public void Write (long number) - { - DoValidation (Condition.Value); - PutNewline (); - - Put (Convert.ToString (number, number_format)); - - context.ExpectingValue = false; - } - - public void Write (string str) - { - DoValidation (Condition.Value); - PutNewline (); - - if (str == null) - Put ("null"); - else - PutString (str); - - context.ExpectingValue = false; - } - - public void Write (ulong number) - { - DoValidation (Condition.Value); - PutNewline (); - - Put (Convert.ToString (number, number_format)); - - context.ExpectingValue = false; - } - - public void WriteArrayEnd () - { - DoValidation (Condition.InArray); - PutNewline (false); - - ctx_stack.Pop (); - if (ctx_stack.Count == 1) - has_reached_end = true; - else { - context = ctx_stack.Peek (); - context.ExpectingValue = false; - } - - Unindent (); - Put ("]"); - } - - public void WriteArrayStart () - { - DoValidation (Condition.NotAProperty); - PutNewline (); - - Put ("["); - - context = new WriterContext (); - context.InArray = true; - ctx_stack.Push (context); - - Indent (); - } - - public void WriteObjectEnd () - { - DoValidation (Condition.InObject); - PutNewline (false); - - ctx_stack.Pop (); - if (ctx_stack.Count == 1) - has_reached_end = true; - else { - context = ctx_stack.Peek (); - context.ExpectingValue = false; - } - - Unindent (); - Put ("}"); - } - - public void WriteObjectStart () - { - DoValidation (Condition.NotAProperty); - PutNewline (); - - Put ("{"); - - context = new WriterContext (); - context.InObject = true; - ctx_stack.Push (context); - - Indent (); - } - - public void WritePropertyName (string property_name) - { - DoValidation (Condition.Property); - PutNewline (); - - PutString (property_name); - - if (pretty_print) { - if (property_name.Length > context.Padding) - context.Padding = property_name.Length; - - for (int i = context.Padding - property_name.Length; - i >= 0; i--) - writer.Write (' '); - - writer.Write (": "); - } else - writer.Write (':'); - - context.ExpectingValue = true; - } - } -} diff --git a/QBox/Json/Lexer.cs b/QBox/Json/Lexer.cs deleted file mode 100644 index 3314227e..00000000 --- a/QBox/Json/Lexer.cs +++ /dev/null @@ -1,910 +0,0 @@ -#region Header -/** - * Lexer.cs - * JSON lexer implementation based on a finite state machine. - * - * The authors disclaim copyright to this source code. For more details, see - * the COPYING file included with this distribution. - **/ -#endregion - - -using System; -using System.Collections.Generic; -using System.IO; -using System.Text; - - -namespace LitJson -{ - internal class FsmContext - { - public bool Return; - public int NextState; - public Lexer L; - public int StateStack; - } - - - internal class Lexer - { - #region Fields - private delegate bool StateHandler (FsmContext ctx); - - private static int[] fsm_return_table; - private static StateHandler[] fsm_handler_table; - - private bool allow_comments; - private bool allow_single_quoted_strings; - private bool end_of_input; - private FsmContext fsm_context; - private int input_buffer; - private int input_char; - private TextReader reader; - private int state; - private StringBuilder string_buffer; - private string string_value; - private int token; - private int unichar; - #endregion - - - #region Properties - public bool AllowComments { - get { return allow_comments; } - set { allow_comments = value; } - } - - public bool AllowSingleQuotedStrings { - get { return allow_single_quoted_strings; } - set { allow_single_quoted_strings = value; } - } - - public bool EndOfInput { - get { return end_of_input; } - } - - public int Token { - get { return token; } - } - - public string StringValue { - get { return string_value; } - } - #endregion - - - #region Constructors - static Lexer () - { - PopulateFsmTables (); - } - - public Lexer (TextReader reader) - { - allow_comments = true; - allow_single_quoted_strings = true; - - input_buffer = 0; - string_buffer = new StringBuilder (128); - state = 1; - end_of_input = false; - this.reader = reader; - - fsm_context = new FsmContext (); - fsm_context.L = this; - } - #endregion - - - #region Static Methods - private static int HexValue (int digit) - { - switch (digit) { - case 'a': - case 'A': - return 10; - - case 'b': - case 'B': - return 11; - - case 'c': - case 'C': - return 12; - - case 'd': - case 'D': - return 13; - - case 'e': - case 'E': - return 14; - - case 'f': - case 'F': - return 15; - - default: - return digit - '0'; - } - } - - private static void PopulateFsmTables () - { - fsm_handler_table = new StateHandler[28] { - State1, - State2, - State3, - State4, - State5, - State6, - State7, - State8, - State9, - State10, - State11, - State12, - State13, - State14, - State15, - State16, - State17, - State18, - State19, - State20, - State21, - State22, - State23, - State24, - State25, - State26, - State27, - State28 - }; - - fsm_return_table = new int[28] { - (int) ParserToken.Char, - 0, - (int) ParserToken.Number, - (int) ParserToken.Number, - 0, - (int) ParserToken.Number, - 0, - (int) ParserToken.Number, - 0, - 0, - (int) ParserToken.True, - 0, - 0, - 0, - (int) ParserToken.False, - 0, - 0, - (int) ParserToken.Null, - (int) ParserToken.CharSeq, - (int) ParserToken.Char, - 0, - 0, - (int) ParserToken.CharSeq, - (int) ParserToken.Char, - 0, - 0, - 0, - 0 - }; - } - - private static char ProcessEscChar (int esc_char) - { - switch (esc_char) { - case '"': - case '\'': - case '\\': - case '/': - return Convert.ToChar (esc_char); - - case 'n': - return '\n'; - - case 't': - return '\t'; - - case 'r': - return '\r'; - - case 'b': - return '\b'; - - case 'f': - return '\f'; - - default: - // Unreachable - return '?'; - } - } - - private static bool State1 (FsmContext ctx) - { - while (ctx.L.GetChar ()) { - if (ctx.L.input_char == ' ' || - ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') - continue; - - if (ctx.L.input_char >= '1' && ctx.L.input_char <= '9') { - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 3; - return true; - } - - switch (ctx.L.input_char) { - case '"': - ctx.NextState = 19; - ctx.Return = true; - return true; - - case ',': - case ':': - case '[': - case ']': - case '{': - case '}': - ctx.NextState = 1; - ctx.Return = true; - return true; - - case '-': - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 2; - return true; - - case '0': - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 4; - return true; - - case 'f': - ctx.NextState = 12; - return true; - - case 'n': - ctx.NextState = 16; - return true; - - case 't': - ctx.NextState = 9; - return true; - - case '\'': - if (! ctx.L.allow_single_quoted_strings) - return false; - - ctx.L.input_char = '"'; - ctx.NextState = 23; - ctx.Return = true; - return true; - - case '/': - if (! ctx.L.allow_comments) - return false; - - ctx.NextState = 25; - return true; - - default: - return false; - } - } - - return true; - } - - private static bool State2 (FsmContext ctx) - { - ctx.L.GetChar (); - - if (ctx.L.input_char >= '1' && ctx.L.input_char<= '9') { - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 3; - return true; - } - - switch (ctx.L.input_char) { - case '0': - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 4; - return true; - - default: - return false; - } - } - - private static bool State3 (FsmContext ctx) - { - while (ctx.L.GetChar ()) { - if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') { - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - continue; - } - - if (ctx.L.input_char == ' ' || - ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') { - ctx.Return = true; - ctx.NextState = 1; - return true; - } - - switch (ctx.L.input_char) { - case ',': - case ']': - case '}': - ctx.L.UngetChar (); - ctx.Return = true; - ctx.NextState = 1; - return true; - - case '.': - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 5; - return true; - - case 'e': - case 'E': - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 7; - return true; - - default: - return false; - } - } - return true; - } - - private static bool State4 (FsmContext ctx) - { - ctx.L.GetChar (); - - if (ctx.L.input_char == ' ' || - ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') { - ctx.Return = true; - ctx.NextState = 1; - return true; - } - - switch (ctx.L.input_char) { - case ',': - case ']': - case '}': - ctx.L.UngetChar (); - ctx.Return = true; - ctx.NextState = 1; - return true; - - case '.': - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 5; - return true; - - case 'e': - case 'E': - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 7; - return true; - - default: - return false; - } - } - - private static bool State5 (FsmContext ctx) - { - ctx.L.GetChar (); - - if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') { - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 6; - return true; - } - - return false; - } - - private static bool State6 (FsmContext ctx) - { - while (ctx.L.GetChar ()) { - if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9') { - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - continue; - } - - if (ctx.L.input_char == ' ' || - ctx.L.input_char >= '\t' && ctx.L.input_char <= '\r') { - ctx.Return = true; - ctx.NextState = 1; - return true; - } - - switch (ctx.L.input_char) { - case ',': - case ']': - case '}': - ctx.L.UngetChar (); - ctx.Return = true; - ctx.NextState = 1; - return true; - - case 'e': - case 'E': - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 7; - return true; - - default: - return false; - } - } - - return true; - } - - private static bool State7 (FsmContext ctx) - { - ctx.L.GetChar (); - - if (ctx.L.input_char >= '0' && ctx.L.input_char<= '9') { - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 8; - return true; - } - - switch (ctx.L.input_char) { - case '+': - case '-': - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - ctx.NextState = 8; - return true; - - default: - return false; - } - } - - private static bool State8 (FsmContext ctx) - { - while (ctx.L.GetChar ()) { - if (ctx.L.input_char >= '0' && ctx.L.input_char<= '9') { - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - continue; - } - - if (ctx.L.input_char == ' ' || - ctx.L.input_char >= '\t' && ctx.L.input_char<= '\r') { - ctx.Return = true; - ctx.NextState = 1; - return true; - } - - switch (ctx.L.input_char) { - case ',': - case ']': - case '}': - ctx.L.UngetChar (); - ctx.Return = true; - ctx.NextState = 1; - return true; - - default: - return false; - } - } - - return true; - } - - private static bool State9 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case 'r': - ctx.NextState = 10; - return true; - - default: - return false; - } - } - - private static bool State10 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case 'u': - ctx.NextState = 11; - return true; - - default: - return false; - } - } - - private static bool State11 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case 'e': - ctx.Return = true; - ctx.NextState = 1; - return true; - - default: - return false; - } - } - - private static bool State12 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case 'a': - ctx.NextState = 13; - return true; - - default: - return false; - } - } - - private static bool State13 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case 'l': - ctx.NextState = 14; - return true; - - default: - return false; - } - } - - private static bool State14 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case 's': - ctx.NextState = 15; - return true; - - default: - return false; - } - } - - private static bool State15 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case 'e': - ctx.Return = true; - ctx.NextState = 1; - return true; - - default: - return false; - } - } - - private static bool State16 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case 'u': - ctx.NextState = 17; - return true; - - default: - return false; - } - } - - private static bool State17 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case 'l': - ctx.NextState = 18; - return true; - - default: - return false; - } - } - - private static bool State18 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case 'l': - ctx.Return = true; - ctx.NextState = 1; - return true; - - default: - return false; - } - } - - private static bool State19 (FsmContext ctx) - { - while (ctx.L.GetChar ()) { - switch (ctx.L.input_char) { - case '"': - ctx.L.UngetChar (); - ctx.Return = true; - ctx.NextState = 20; - return true; - - case '\\': - ctx.StateStack = 19; - ctx.NextState = 21; - return true; - - default: - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - continue; - } - } - - return true; - } - - private static bool State20 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case '"': - ctx.Return = true; - ctx.NextState = 1; - return true; - - default: - return false; - } - } - - private static bool State21 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case 'u': - ctx.NextState = 22; - return true; - - case '"': - case '\'': - case '/': - case '\\': - case 'b': - case 'f': - case 'n': - case 'r': - case 't': - ctx.L.string_buffer.Append ( - ProcessEscChar (ctx.L.input_char)); - ctx.NextState = ctx.StateStack; - return true; - - default: - return false; - } - } - - private static bool State22 (FsmContext ctx) - { - int counter = 0; - int mult = 4096; - - ctx.L.unichar = 0; - - while (ctx.L.GetChar ()) { - - if (ctx.L.input_char >= '0' && ctx.L.input_char <= '9' || - ctx.L.input_char >= 'A' && ctx.L.input_char <= 'F' || - ctx.L.input_char >= 'a' && ctx.L.input_char <= 'f') { - - ctx.L.unichar += HexValue (ctx.L.input_char) * mult; - - counter++; - mult /= 16; - - if (counter == 4) { - ctx.L.string_buffer.Append ( - Convert.ToChar (ctx.L.unichar)); - ctx.NextState = ctx.StateStack; - return true; - } - - continue; - } - - return false; - } - - return true; - } - - private static bool State23 (FsmContext ctx) - { - while (ctx.L.GetChar ()) { - switch (ctx.L.input_char) { - case '\'': - ctx.L.UngetChar (); - ctx.Return = true; - ctx.NextState = 24; - return true; - - case '\\': - ctx.StateStack = 23; - ctx.NextState = 21; - return true; - - default: - ctx.L.string_buffer.Append ((char) ctx.L.input_char); - continue; - } - } - - return true; - } - - private static bool State24 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case '\'': - ctx.L.input_char = '"'; - ctx.Return = true; - ctx.NextState = 1; - return true; - - default: - return false; - } - } - - private static bool State25 (FsmContext ctx) - { - ctx.L.GetChar (); - - switch (ctx.L.input_char) { - case '*': - ctx.NextState = 27; - return true; - - case '/': - ctx.NextState = 26; - return true; - - default: - return false; - } - } - - private static bool State26 (FsmContext ctx) - { - while (ctx.L.GetChar ()) { - if (ctx.L.input_char == '\n') { - ctx.NextState = 1; - return true; - } - } - - return true; - } - - private static bool State27 (FsmContext ctx) - { - while (ctx.L.GetChar ()) { - if (ctx.L.input_char == '*') { - ctx.NextState = 28; - return true; - } - } - - return true; - } - - private static bool State28 (FsmContext ctx) - { - while (ctx.L.GetChar ()) { - if (ctx.L.input_char == '*') - continue; - - if (ctx.L.input_char == '/') { - ctx.NextState = 1; - return true; - } - - ctx.NextState = 27; - return true; - } - - return true; - } - #endregion - - - private bool GetChar () - { - if ((input_char = NextChar ()) != -1) - return true; - - end_of_input = true; - return false; - } - - private int NextChar () - { - if (input_buffer != 0) { - int tmp = input_buffer; - input_buffer = 0; - - return tmp; - } - - return reader.Read (); - } - - public bool NextToken () - { - StateHandler handler; - fsm_context.Return = false; - - while (true) { - handler = fsm_handler_table[state - 1]; - - if (! handler (fsm_context)) - throw new JsonException (input_char); - - if (end_of_input) - return false; - - if (fsm_context.Return) { - string_value = string_buffer.ToString (); - string_buffer.Remove (0, string_buffer.Length); - token = fsm_return_table[state - 1]; - - if (token == (int) ParserToken.Char) - token = input_char; - - state = fsm_context.NextState; - - return true; - } - - state = fsm_context.NextState; - } - } - - private void UngetChar () - { - input_buffer = input_char; - } - } -} diff --git a/QBox/Json/ParserToken.cs b/QBox/Json/ParserToken.cs deleted file mode 100644 index 9aaee977..00000000 --- a/QBox/Json/ParserToken.cs +++ /dev/null @@ -1,44 +0,0 @@ -#region Header -/** - * ParserToken.cs - * Internal representation of the tokens used by the lexer and the parser. - * - * The authors disclaim copyright to this source code. For more details, see - * the COPYING file included with this distribution. - **/ -#endregion - - -namespace LitJson -{ - internal enum ParserToken - { - // Lexer tokens - None = System.Char.MaxValue + 1, - Number, - True, - False, - Null, - CharSeq, - // Single char - Char, - - // Parser Rules - Text, - Object, - ObjectPrime, - Pair, - PairRest, - Array, - ArrayPrime, - Value, - ValueRest, - String, - - // End of input - End, - - // The empty rule - Epsilon - } -} diff --git a/QBox/RPC/CallRet.cs b/QBox/RPC/CallRet.cs deleted file mode 100644 index 27f3624f..00000000 --- a/QBox/RPC/CallRet.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Net; - -namespace QBox.RPC -{ - public class CallRet - { - public HttpStatusCode StatusCode { get; protected set; } - public Exception Exception { get; protected set; } - public string Response { get; protected set; } - public bool OK { get { return (int)StatusCode / 100 == 2; } } - - public CallRet(HttpStatusCode statusCode, string response) - { - StatusCode = statusCode; - Response = response; - } - - public CallRet(HttpStatusCode statusCode, Exception e) - { - StatusCode = statusCode; - Exception = e; - } - - public CallRet(CallRet ret) - { - StatusCode = ret.StatusCode; - Exception = ret.Exception; - Response = ret.Response; - } - } -} diff --git a/QBox/RPC/Client.cs b/QBox/RPC/Client.cs deleted file mode 100644 index efc7950b..00000000 --- a/QBox/RPC/Client.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Net; -using System.IO; -using QBox.Util; - -namespace QBox.RPC -{ - public class Client - { - public virtual void SetAuth(HttpWebRequest request, Stream body) { } - - public CallRet Call(string url) - { - Console.WriteLine("Client.Post ==> URL: " + url); - try - { - HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); - request.Method = "POST"; - SetAuth(request, null); - using (HttpWebResponse response = request.GetResponse() as HttpWebResponse) - { - return HandleResult(response); - } - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - return new CallRet(HttpStatusCode.BadRequest, e); - } - } - - public CallRet CallWithBinary(string url, string contentType, Stream body, long length) - { - Console.WriteLine("Client.PostWithBinary ==> URL: {0} Length:{1}", url, length); - try - { - HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); - request.Method = "POST"; - request.ContentType = contentType; - request.ContentLength = length; - SetAuth(request, body); - using (Stream requestStream = request.GetRequestStream()) - { - StreamUtil.CopyN(body, requestStream, length); - } - using (HttpWebResponse response = request.GetResponse() as HttpWebResponse) - { - return HandleResult(response); - } - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - return new CallRet(HttpStatusCode.BadRequest, e); - } - } - - public static CallRet HandleResult(HttpWebResponse response) - { - HttpStatusCode statusCode = response.StatusCode; - using (StreamReader reader = new StreamReader(response.GetResponseStream())) - { - string responseStr = reader.ReadToEnd(); - return new CallRet(statusCode, responseStr); - } - } - } -} diff --git a/QBox/RS/Config.cs b/QBox/RS/Config.cs deleted file mode 100644 index f40d7fd3..00000000 --- a/QBox/RS/Config.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace QBox.RS -{ - public class Config - { - public static string ACCESS_KEY = ""; - public static string SECRET_KEY = ""; - - public static string IO_HOST = "http://iovip.qbox.me"; - public static string RS_HOST = "http://rs.qbox.me:10100"; - public static string UP_HOST = "http://up.qbox.me"; - - public static int PUT_RETRY_TIMES = 3; - } -} diff --git a/QBox/RS/FileParameter.cs b/QBox/RS/FileParameter.cs deleted file mode 100644 index 5de705ce..00000000 --- a/QBox/RS/FileParameter.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; - -namespace QBox.RS -{ - public class FileParameter - { - public string FileName { get; set; } - public string MimeType { get; set; } - - public FileParameter(string fname, string mimeType) - { - FileName = fname; - MimeType = mimeType; - } - } -} diff --git a/QBox/RS/GetRet.cs b/QBox/RS/GetRet.cs deleted file mode 100644 index b9b2bac2..00000000 --- a/QBox/RS/GetRet.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System; -using System.Net; -using LitJson; -using QBox.RPC; - -namespace QBox.RS -{ - public class GetRet : CallRet - { - public string Hash { get; private set; } - public long FileSize { get; private set; } - public string MimeType { get; private set; } - public string Url { get; private set; } - - public GetRet(CallRet ret) - : base(ret) - { - if (OK && !String.IsNullOrEmpty(Response)) - { - try - { - Unmarshal(Response); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - this.Exception = e; - } - } - } - - private void Unmarshal(string json) - { - JsonData data = JsonMapper.ToObject(json); - Hash = (string)data["hash"]; - JsonData fsize = data["fsize"]; - if (fsize.IsInt) - FileSize = (int)fsize; - else if (fsize.IsLong) - FileSize = (long)fsize; - MimeType = (string)data["mimeType"]; - Url = (string)data["url"]; - } - } -} diff --git a/QBox/RS/MultiPartFormDataPost.cs b/QBox/RS/MultiPartFormDataPost.cs deleted file mode 100644 index 400f2c95..00000000 --- a/QBox/RS/MultiPartFormDataPost.cs +++ /dev/null @@ -1,122 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; -using System.Net; -using QBox.Util; -using QBox.RPC; - -namespace QBox.RS -{ - public static class MultiPartFormDataPost - { - public static Encoding encoding = Encoding.ASCII; - - private static long GetContentLength(Dictionary postParams, string boundary) - { - long length = 0; - bool needsCLRF = false; - foreach (var param in postParams) - { - if (needsCLRF) - length += encoding.GetByteCount("\r\n"); - - needsCLRF = true; - - if (param.Value is FileParameter) - { - FileParameter fileToUpload = (FileParameter)param.Value; - - string headfmt = "--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\";\r\nContent-Type: {3}\r\n\r\n"; - string header = string.Format(headfmt, boundary, param.Key, - fileToUpload.FileName ?? param.Key, - fileToUpload.MimeType ?? "application/octet-stream"); - - length += encoding.GetByteCount(header); - using (FileStream fs = File.OpenRead(fileToUpload.FileName)) - { - length += fs.Length; - } - } - else - { - string headfmt = "--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}"; - string partData = string.Format(headfmt, boundary, param.Key, param.Value); - length += encoding.GetByteCount(partData); - } - } - - string footer = "\r\n--" + boundary + "--\r\n"; - length += encoding.GetByteCount(footer); - return length; - } - - private static void WriteBody(Dictionary postParams, string boundary, Stream body) - { - bool needsCLRF = false; - foreach (var param in postParams) - { - if (needsCLRF) - body.Write(encoding.GetBytes("\r\n"), 0, encoding.GetByteCount("\r\n")); - - needsCLRF = true; - - if (param.Value is FileParameter) - { - FileParameter fileToUpload = (FileParameter)param.Value; - - string headfmt = "--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\";\r\nContent-Type: {3}\r\n\r\n"; - string header = string.Format(headfmt, boundary, param.Key, - fileToUpload.FileName ?? param.Key, - fileToUpload.MimeType ?? "application/octet-stream"); - - body.Write(encoding.GetBytes(header), 0, encoding.GetByteCount(header)); - using (FileStream fs = File.OpenRead(fileToUpload.FileName)) - { - StreamUtil.Copy(fs, body); - } - } - else - { - string headfmt = "--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}"; - string partData = string.Format(headfmt, boundary, param.Key, param.Value); - body.Write(encoding.GetBytes(partData), 0, encoding.GetByteCount(partData)); - } - } - - string footer = "\r\n--" + boundary + "--\r\n"; - body.Write(encoding.GetBytes(footer), 0, encoding.GetByteCount(footer)); - } - - public static CallRet Post(string url, Dictionary postParams) - { - Console.WriteLine("URL: " + url); - string boundary = String.Format("----------{0:N}", Guid.NewGuid()); - string contentType = "multipart/form-data; boundary=" + boundary; - - Stream requestStream = null; - HttpWebResponse response = null; - try - { - HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url); - request.Method = "POST"; - request.ContentType = contentType; - request.ContentLength = GetContentLength(postParams, boundary); - requestStream = request.GetRequestStream(); - WriteBody(postParams, boundary, requestStream); - response = request.GetResponse() as HttpWebResponse; - return Client.HandleResult(response); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - return new CallRet(HttpStatusCode.BadRequest, e); - } - finally - { - requestStream.Close(); - response.Close(); - } - } - } -} diff --git a/QBox/RS/PutAuthRet.cs b/QBox/RS/PutAuthRet.cs deleted file mode 100644 index 0d4361b5..00000000 --- a/QBox/RS/PutAuthRet.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System; -using LitJson; -using QBox.RPC; - -namespace QBox.RS -{ - public class PutAuthRet : CallRet - { - public int Expires { get; private set; } - public string Url { get; private set; } - - public PutAuthRet(CallRet ret) - : base(ret) - { - if (!String.IsNullOrEmpty(Response)) - { - try - { - Unmarshal(Response); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - this.Exception = e; - } - } - } - - private void Unmarshal(string json) - { - JsonData data = JsonMapper.ToObject(json); - Expires = (int)data["expiresIn"]; - Url = (string)data["url"]; - } - } -} diff --git a/QBox/RS/PutFileRet.cs b/QBox/RS/PutFileRet.cs deleted file mode 100644 index eaba1389..00000000 --- a/QBox/RS/PutFileRet.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using LitJson; -using QBox.RPC; - -namespace QBox.RS -{ - public class PutFileRet : CallRet - { - public string Hash { get; private set; } - - public PutFileRet(CallRet ret) - : base(ret) - { - if (!String.IsNullOrEmpty(Response)) - { - try - { - Unmarshal(Response); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - this.Exception = e; - } - } - } - - private void Unmarshal(string json) - { - JsonData data = JsonMapper.ToObject(json); - Hash = (string)data["hash"]; - } - } -} diff --git a/QBox/RS/RSClient.cs b/QBox/RS/RSClient.cs deleted file mode 100644 index e08a6c3b..00000000 --- a/QBox/RS/RSClient.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using QBox.Util; -using QBox.RPC; - -namespace QBox.RS -{ - public class RSClient - { - public static PutFileRet PutFile( - string url, string bucketName, string key, string mimeType, - string localFile, string customMeta, string callbackParam) - { - string entryURI = bucketName + ":" + key; - if (String.IsNullOrEmpty(mimeType)) - { - mimeType = "application/octet-stream"; - } - string action = "/rs-put/" + Base64UrlSafe.Encode(entryURI) + - "/mimeType/" + Base64UrlSafe.Encode(mimeType); - if (!String.IsNullOrEmpty(customMeta)) - { - action += "/meta/" + Base64UrlSafe.Encode(customMeta); - } - - try - { - var postParams = new Dictionary(); - postParams["action"] = action; - postParams["file"] = new FileParameter(localFile, mimeType); - if (!String.IsNullOrEmpty(callbackParam)) - postParams["params"] = callbackParam; - CallRet callRet = MultiPartFormDataPost.Post(url, postParams); - return new PutFileRet(callRet); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - return new PutFileRet(new CallRet(HttpStatusCode.BadRequest, e)); - } - } - - public static PutFileRet PutFileWithUpToken( - string upToken, string tableName, string key, string mimeType, - string localFile, string customMeta, string callbackParam) - { - return PutFileWithUpToken(upToken, tableName, key, - mimeType, localFile, customMeta, callbackParam, 0); - } - - public static PutFileRet PutFileWithUpToken( - string upToken, string tableName, string key, string mimeType, - string localFile, string customMeta, string callbackParam, UInt32 crc32) - { - string entryURI = tableName + ":" + key; - if (String.IsNullOrEmpty(mimeType)) - { - mimeType = "application/octet-stream"; - } - string action = "/rs-put/" + Base64UrlSafe.Encode(entryURI) + - "/mimeType/" + Base64UrlSafe.Encode(mimeType); - if (!String.IsNullOrEmpty(customMeta)) - { - action += "/meta/" + Base64UrlSafe.Encode(customMeta); - } - if (crc32 != 0) - { - action += "/crc32/" + crc32.ToString(); - } - - try - { - var postParams = new Dictionary(); - postParams["auth"] = upToken; - postParams["action"] = action; - postParams["file"] = new FileParameter(localFile, mimeType); - if (!String.IsNullOrEmpty(callbackParam)) - postParams["params"] = callbackParam; - CallRet callRet = MultiPartFormDataPost.Post(Config.UP_HOST + "/upload", postParams); - return new PutFileRet(callRet); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - return new PutFileRet(new CallRet(HttpStatusCode.BadRequest, e)); - } - } - } -} diff --git a/QBox/RS/RSService.cs b/QBox/RS/RSService.cs deleted file mode 100644 index dc40d1e8..00000000 --- a/QBox/RS/RSService.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using System.IO; -using System.Net; -using QBox.FileOp; -using QBox.Util; -using QBox.RPC; - -namespace QBox.RS -{ - public class RSService - { - public Client Conn { get; private set; } - public string BucketName { get; private set; } - - public RSService(Client conn, string bucketName) - { - Conn = conn; - BucketName = bucketName; - } - - public CallRet MkBucket() - { - string url = Config.RS_HOST + "/mkbucket/" + BucketName; - return Conn.Call(url); - } - - public PutAuthRet PutAuth() - { - CallRet callRet = Conn.Call(Config.IO_HOST + "/put-auth/"); - return new PutAuthRet(callRet); - } - - public PutFileRet PutFile(string key, string mimeType, string localFile, string customMeta) - { - string entryURI = BucketName + ":" + key; - if (String.IsNullOrEmpty(mimeType)) - { - mimeType = "application/octet-stream"; - } - string url = Config.IO_HOST + "/rs-put/" + Base64UrlSafe.Encode(entryURI) + - "/mimeType/" + Base64UrlSafe.Encode(mimeType); - if (!String.IsNullOrEmpty(customMeta)) - { - url += "/meta/" + Base64UrlSafe.Encode(customMeta); - } - - try - { - using (FileStream fs = File.OpenRead(localFile)) - { - CallRet callRet = Conn.CallWithBinary(url, mimeType, fs, fs.Length); - return new PutFileRet(callRet); - } - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - return new PutFileRet(new CallRet(HttpStatusCode.BadRequest, e)); - } - } - - public GetRet Get(string key, string attName) - { - string entryURI = BucketName + ":" + key; - string url = Config.RS_HOST + "/get/" + Base64UrlSafe.Encode(entryURI) + "/attName/" - + Base64UrlSafe.Encode(attName); - CallRet callRet = Conn.Call(url); - return new GetRet(callRet); - } - - public GetRet GetIfNotModified(string key, string attName, string hash) - { - string entryURI = BucketName + ":" + key; - string url = Config.RS_HOST + "/get/" + Base64UrlSafe.Encode(entryURI) + "/attName/" - + Base64UrlSafe.Encode(attName) + "/base/" + hash; - CallRet callRet = Conn.Call(url); - return new GetRet(callRet); - } - - public StatRet Stat(string key) - { - string entryURI = BucketName + ":" + key; - string url = Config.RS_HOST + "/stat/" + Base64UrlSafe.Encode(entryURI); - CallRet callRet = Conn.Call(url); - return new StatRet(callRet); - } - - public CallRet Publish(string domain) - { - String url = Config.RS_HOST + "/publish/" + Base64UrlSafe.Encode(domain) + "/from/" + BucketName; - return Conn.Call(url); - } - - public CallRet Unpublish(string domain) - { - string url = Config.RS_HOST + "/unpublish/" + Base64UrlSafe.Encode(domain); - return Conn.Call(url); - } - - public CallRet Delete(string key) - { - string entryURI = BucketName + ":" + key; - string url = Config.RS_HOST + "/delete/" + Base64UrlSafe.Encode(entryURI); - return Conn.Call(url); - } - - public CallRet Drop() - { - string url = Config.RS_HOST + "/drop/" + BucketName; - return Conn.Call(url); - } - - public PutFileRet SaveAs(string url, string specStr, string key) - { - string entryURI = BucketName + ":" + key; - url = url + specStr + "/save-as/" + Base64UrlSafe.Encode(entryURI); - CallRet callRet = Conn.Call(url); - return new PutFileRet(callRet); - } - - public PutFileRet ImageMogrifySaveAs(string url, ImageMogrifySpec spec, string key) - { - string specStr = spec.MakeSpecString(); - return SaveAs(url, specStr, key); - } - } -} diff --git a/QBox/RS/ResumablePut.cs b/QBox/RS/ResumablePut.cs deleted file mode 100644 index a74bfaa6..00000000 --- a/QBox/RS/ResumablePut.cs +++ /dev/null @@ -1,96 +0,0 @@ -using System; -using System.IO; -using System.Text; -using QBox.Util; -using QBox.RPC; - -namespace QBox.RS -{ - public static class ResumablePut - { - private static int ChunkBits = 22; - private static long ChunkSize = 1 << ChunkBits; - - public static ResumablePutFileRet Mkblock(string host, Client client, Stream body, long length) - { - string url = host + "/mkblk/" + Convert.ToString(length); - CallRet callRet = client.CallWithBinary(url, "application/octet-stream", body, length); - return new ResumablePutFileRet(callRet); - } - - public static PutFileRet Mkfile(string host, Client client, string entryURI, long fsize, - string customMeta, string callbackParam, string[] ctxs) - { - string url = host + "/rs-mkfile/" + Base64UrlSafe.Encode(entryURI) + - "/fsize/" + Convert.ToString(fsize); - if (!String.IsNullOrEmpty(callbackParam)) - { - url += "/params/" + Base64UrlSafe.Encode(callbackParam); - } - if (!String.IsNullOrEmpty(customMeta)) - { - url += "/meta/" + Base64UrlSafe.Encode(customMeta); - } - - using (Stream body = new MemoryStream()) - { - for (int i = 0; i < ctxs.Length; i++) - { - byte[] bctx = Encoding.ASCII.GetBytes(ctxs[i]); - body.Write(bctx, 0, bctx.Length); - if (i != ctxs.Length-1) - { - body.WriteByte((byte)','); - } - } - body.Seek(0, SeekOrigin.Begin); - CallRet ret= client.CallWithBinary(url, "text/plain", body, body.Length); - return new PutFileRet(ret); - } - } - - public static PutFileRet PutFile( - Client client, string tableName, string key, string mimeType, - string localFile, string customMeta, string callbackParam) - { - long fsize = 0; - string[] ctxs = null; - string host = Config.UP_HOST; - using (FileStream fs = File.OpenRead(localFile)) - { - fsize = fs.Length; - int chunkCnt = (int)((fsize + (ChunkSize - 1)) >> ChunkBits); - long chunkSize = ChunkSize; - ctxs = new string[chunkCnt]; - Console.WriteLine("ResumablePut ==> fsize: {0}, chunkCnt: {1}", fsize, chunkCnt); - for (int i = 0; i < chunkCnt; i++) - { - if (i == chunkCnt - 1) - { - chunkSize = fsize - (i << ChunkBits); - } - ResumablePutFileRet ret = null; - for (int retry = 0; retry < Config.PUT_RETRY_TIMES; retry++) - { - fs.Seek(i * ChunkSize, SeekOrigin.Begin); - ret = Mkblock(host, client, fs, chunkSize); - if (ret.OK) - { - ctxs[i] = ret.Ctx; - host = ret.Host; - break; - } - } - if (!ret.OK) - { - Console.WriteLine(ret.Exception.ToString()); - return new PutFileRet(new CallRet(ret)); - } - } - } - - string entryURI = tableName + ":" + key; - return Mkfile(host, client, entryURI, fsize, customMeta, callbackParam, ctxs); - } - } -} diff --git a/QBox/RS/ResumablePutFileRet.cs b/QBox/RS/ResumablePutFileRet.cs deleted file mode 100644 index 5140712a..00000000 --- a/QBox/RS/ResumablePutFileRet.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using LitJson; -using QBox.RPC; - -namespace QBox.RS -{ - public class ResumablePutFileRet : CallRet - { - public string Ctx { get; private set; } - public string Checksum { get; private set; } - public string Host { get; private set; } - - public ResumablePutFileRet(CallRet ret) - : base(ret) - { - if (!String.IsNullOrEmpty(Response)) - { - try - { - Unmarshal(Response); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - this.Exception = e; - } - } - } - - private void Unmarshal(string json) - { - JsonData data = JsonMapper.ToObject(json); - Ctx = (string)data["ctx"]; - Checksum = (string)data["checksum"]; - Host = (string)data["host"]; - } - } -} diff --git a/QBox/RS/StatRet.cs b/QBox/RS/StatRet.cs deleted file mode 100644 index 0ce1c12d..00000000 --- a/QBox/RS/StatRet.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System; -using LitJson; -using QBox.RPC; - -namespace QBox.RS -{ - public class StatRet : CallRet - { - public string Hash { get; private set; } - public long FileSize { get; private set; } - public long PutTime { get; private set; } - public string MimeType { get; private set; } - - public StatRet(CallRet ret) - : base(ret) - { - if (OK && Response != null) - { - try - { - Unmarshal(Response); - } - catch (Exception e) - { - Console.WriteLine(e.ToString()); - this.Exception = e; - } - } - } - - private void Unmarshal(string json) - { - JsonData data = JsonMapper.ToObject(json); - Hash = (string)data["hash"]; - JsonData fsize = data["fsize"]; - if (fsize.IsInt) - FileSize = (int)fsize; - else if (fsize.IsLong) - FileSize = (long)fsize; - JsonData putTime = data["putTime"]; - if (putTime.IsInt) - PutTime = (int)putTime; - else if (putTime.IsLong) - PutTime = (long)putTime; - MimeType = (string)data["mimeType"]; - } - } -} diff --git a/QBox/Util/Base64UrlSafe.cs b/QBox/Util/Base64UrlSafe.cs deleted file mode 100644 index 5e242937..00000000 --- a/QBox/Util/Base64UrlSafe.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Text; - -namespace QBox.Util -{ - public static class Base64UrlSafe - { - public static string Encode(string text) - { - if (String.IsNullOrEmpty(text)) return ""; - byte[] bs = Encoding.UTF8.GetBytes(text); - string encodedStr = Convert.ToBase64String(bs); - encodedStr = encodedStr.Replace('+', '-').Replace('/', '_'); - return encodedStr; - } - - public static string Encode(byte[] bs) - { - if (bs == null || bs.Length == 0) return ""; - string encodedStr = Convert.ToBase64String(bs); - encodedStr = encodedStr.Replace('+', '-').Replace('/', '_'); - return encodedStr; - } - } -} diff --git a/QBox/Util/CRC32.cs b/QBox/Util/CRC32.cs deleted file mode 100644 index 5cc0b064..00000000 --- a/QBox/Util/CRC32.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System; -using System.IO; - -namespace QBox.Util -{ - public class CRC32 - { - public const UInt32 IEEE = 0xedb88320; - - private UInt32[] Table; - private UInt32 Value; - - public CRC32() - { - Value = 0; - Table = MakeTable(IEEE); - } - - public void Write(byte[] p, int offset, int count) - { - this.Value = Update(this.Value, this.Table, p, offset, count); - } - - public UInt32 Sum32() - { - return this.Value; - } - - private static UInt32[] MakeTable(UInt32 poly) - { - UInt32[] table = new UInt32[256]; - for (int i = 0; i < 256; i++) - { - UInt32 crc = (UInt32)i; - for (int j = 0; j < 8; j++) - { - if ((crc & 1) == 1) - crc = (crc >> 1) ^ poly; - else - crc >>= 1; - } - table[i] = crc; - } - return table; - } - - public static UInt32 Update(UInt32 crc, UInt32[] table, byte[] p, int offset, int count) - { - crc = ~crc; - for (int i = 0; i < count; i++) - { - crc = table[((byte)crc) ^ p[offset+i]] ^ (crc >> 8); - } - return ~crc; - } - - public static UInt32 CheckSumBytes(byte[] data) - { - CRC32 crc = new CRC32(); - crc.Write(data, 0, data.Length); - return crc.Sum32(); - } - - public static UInt32 CheckSumFile(string fileName) - { - CRC32 crc = new CRC32(); - int bufferLen = 32*1024; - using (FileStream fs = File.OpenRead(fileName)) - { - byte[] buffer = new byte[bufferLen]; - while (true) - { - int n = fs.Read(buffer, 0, bufferLen); - if (n == 0) break; - crc.Write(buffer, 0, n); - } - } - return crc.Sum32(); - } - } -} diff --git a/QBox/app.config b/QBox/app.config deleted file mode 100644 index cb2586be..00000000 --- a/QBox/app.config +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/Qiniu.Test/Auth/PutPolicy.cs b/Qiniu.Test/Auth/PutPolicy.cs new file mode 100644 index 00000000..6b1c2988 --- /dev/null +++ b/Qiniu.Test/Auth/PutPolicy.cs @@ -0,0 +1,43 @@ +using System; +using NUnit.Framework; +using Qiniu.RS; +using Qiniu.Auth.digest; + +namespace Qiniu.Test.FileOp +{ + + + /// + ///这是 GetPolicyTest 的测试类,旨在 + ///包含所有 GetPolicyTest 单元测试 + /// + [TestFixture] + public class GetPolicyTest:QiniuTestBase + { + /// + ///MakeRequest 的测试 + /// + [Test] + public void MakeRequestTest() + { + string actual; + actual = GetPolicy.MakeRequest(FileOpUrl); + //System.Diagnostics.Process.Start(actual); + PrintLn(actual); + Assert.IsTrue(!string.IsNullOrEmpty(actual), "GetPolicyTest MakeRequestTest Failure"); + } + + /// + ///MakeBaseUrl 的测试 + /// + [Test] + public void MakeBaseUrlTest() + { + string actual; + actual = GetPolicy.MakeBaseUrl(Bucket+".qiniudn.com", LocalKey); + //System.Diagnostics.Process.Start(actual); + PrintLn(actual); + Assert.IsTrue(!string.IsNullOrEmpty(actual), "GetPolicyTest MakeBaseUrlTest Failure"); + } + } +} diff --git a/Qiniu.Test/FileOp/ExifTest.cs b/Qiniu.Test/FileOp/ExifTest.cs new file mode 100644 index 00000000..cbeba75e --- /dev/null +++ b/Qiniu.Test/FileOp/ExifTest.cs @@ -0,0 +1,27 @@ +using Qiniu.FileOp; +using Qiniu.RS; +using NUnit.Framework; +using System; + +namespace Qiniu.Test.FileOp +{ + /// + ///这是 ExifTest 的测试类,旨在 + ///包含所有 ExifTest 单元测试 + /// + [TestFixture] + public class ExifTest:QiniuTestBase + { + /// + ///MakeRequest 的测试 + /// + [Test] + public void MakeRequestTest () + { + string url = GetPolicy.MakeBaseUrl ("qiniuphotos.qiniudn.com", "gogopher.jpg"); // TODO: 初始化为适当的值 + string actual = Exif.MakeRequest (url); + ExifRet ret = Exif.Call (actual); + Assert.IsTrue (ret.OK, "MakeRequestTest Failure"); + } + } +} diff --git a/Qiniu.Test/FileOp/ImageInfoTest.cs b/Qiniu.Test/FileOp/ImageInfoTest.cs new file mode 100644 index 00000000..c49f4fe3 --- /dev/null +++ b/Qiniu.Test/FileOp/ImageInfoTest.cs @@ -0,0 +1,30 @@ +using System; +using Qiniu.FileOp; +using Qiniu.RS; +using NUnit.Framework; + +namespace Qiniu.Test.FileOp +{ + + + /// + ///这是 ImageInfoTest 的测试类,旨在 + ///包含所有 ImageInfoTest 单元测试 + /// + [TestFixture] + public class ImageInfoTest:QiniuTestBase + { + /// + ///MakeRequest 的测试 + /// + [Test] + public void MakeRequestTest() + { + string actual; + actual = ImageInfo.MakeRequest(FileOpUrl); + //System.Diagnostics.Process.Start(actual); + ImageInfoRet ret= ImageInfo.Call(actual); + Assert.IsNotNull(ret, "ImageInfoTest MakeRequestTest Failure"); + } + } +} diff --git a/Qiniu.Test/FileOp/ImageMogrifyTest.cs b/Qiniu.Test/FileOp/ImageMogrifyTest.cs new file mode 100644 index 00000000..30dcceef --- /dev/null +++ b/Qiniu.Test/FileOp/ImageMogrifyTest.cs @@ -0,0 +1,38 @@ +using System; +using Qiniu.FileOp; +using Qiniu.RS; +using NUnit.Framework; + +namespace Qiniu.Test.FileOp +{ + + + /// + ///这是 ImageMogrifyTest 的测试类,旨在 + ///包含所有 ImageMogrifyTest 单元测试 + /// + [TestFixture] + public class ImageMogrifyTest:QiniuTestBase + { + /// + ///MakeRequest 的测试 + /// + [Test] + public void MakeRequestTest() + { + ImageMogrify target = new ImageMogrify + { + Thumbnail = "!50x50r", + Gravity = "center", + Rotate = 90, + Crop = "!50x50", + Quality = 80, + AutoOrient = true + }; + string mogrUrl = target.MakeRequest(FileOpUrl); + //System.Diagnostics.Process.Start(mogrUrl); + PrintLn(mogrUrl); + Assert.IsTrue(!string.IsNullOrEmpty(mogrUrl), "ImageMogrifyTest MakeRequestTest Failure"); + } + } +} diff --git a/Qiniu.Test/FileOp/ImageViewTest.cs b/Qiniu.Test/FileOp/ImageViewTest.cs new file mode 100644 index 00000000..15f665a0 --- /dev/null +++ b/Qiniu.Test/FileOp/ImageViewTest.cs @@ -0,0 +1,32 @@ +using System; +using Qiniu.FileOp; +using Qiniu.RS; +using NUnit.Framework; + +namespace Qiniu.Test.FileOp +{ + + + /// + ///这是 ImageViewTest 的测试类,旨在 + ///包含所有 ImageViewTest 单元测试 + /// + [TestFixture] + public class ImageViewTest:QiniuTestBase + { + /// + ///MakeRequest 的测试 + /// + [Test] + public void MakeRequestTest() + { + ImageView target = new ImageView { Mode = 0, Width = 200, Height = 200, Quality = 90, Format = "gif" }; // TODO: 初始化为适当的值 + string url = FileOpUrl; // TODO: 初始化为适当的值 + string actual; + actual = target.MakeRequest(url); + //System.Diagnostics.Process.Start(actual); + Assert.IsTrue(!string.IsNullOrEmpty(actual), "ImageViewTest MakeRequestTest Failure"); + + } + } +} diff --git a/Qiniu.Test/FileOp/ImageWaterMarkerTest.cs b/Qiniu.Test/FileOp/ImageWaterMarkerTest.cs new file mode 100644 index 00000000..f2656ae2 --- /dev/null +++ b/Qiniu.Test/FileOp/ImageWaterMarkerTest.cs @@ -0,0 +1,36 @@ +using System; +using Qiniu.FileOp; +using Qiniu.RS; +using NUnit.Framework; + +namespace Qiniu.Test.FileOp +{ + + + /// + ///这是 ImageWaterMarkerTest 的测试类,旨在 + ///包含所有 ImageWaterMarkerTest 单元测试 + /// + //[TestFixture] + public class ImageWaterMarkerTest:QiniuTestBase + { + /// + ///MakeRequest 的测试 + /// + //[Test] + public void MakeRequestTest() + { + string imageUrl = "http://www.b1.qiniudn.com/images/logo-2.png"; // TODO: 初始化为适当的值 + int dissolve = 50; // TODO: 初始化为适当的值 + ImageWaterMarker target = new ImageWaterMarker(imageUrl,dissolve,MarkerGravity.Center); // TODO: 初始化为适当的值 + string actual; + actual = target.MakeRequest(FileOpUrl); + // 如果是私有空间,添加下面一句 + actual = GetPolicy.MakeRequest(actual); + //System.Diagnostics.Process.Start(actual); + PrintLn(actual); + Assert.IsTrue(!string.IsNullOrEmpty(actual), "ImageWaterMarkerTest MakeRequestTest Failure"); + + } + } +} diff --git a/Qiniu.Test/FileOp/TextWaterMarkerTest.cs b/Qiniu.Test/FileOp/TextWaterMarkerTest.cs new file mode 100644 index 00000000..b3377826 --- /dev/null +++ b/Qiniu.Test/FileOp/TextWaterMarkerTest.cs @@ -0,0 +1,41 @@ +using System; +using Qiniu.FileOp; +using Qiniu.RS; +using NUnit.Framework; + +namespace Qiniu.Test.FileOp +{ + + + /// + ///这是 TextWaterMarkerTest 的测试类,旨在 + ///包含所有 TextWaterMarkerTest 单元测试 + /// + [TestFixture] + public class TextWaterMarkerTest:QiniuTestBase + { + /// + ///MakeRequest 的测试 + /// + [Test] + public void MakeRequestTest() + { + string text = "Qiniu"; // TODO: 初始化为适当的值 + string fontname = string.Empty; // TODO: 初始化为适当的值 + string color = "#123abc"; // TODO: 初始化为适当的值 + int fontsize = 2000; // TODO: 初始化为适当的值 + int dissolve = 50; // TODO: 初始化为适当的值 + int dx = 0; // TODO: 初始化为适当的值 + int dy = 0; // TODO: 初始化为适当的值 + TextWaterMarker target = new TextWaterMarker(text, fontname, color, fontsize, dissolve, MarkerGravity.South, dx, dy); // TODO: 初始化为适当的值 + + string actual; + actual = target.MakeRequest(FileOpUrl); + // 如果是私有空间,添加下面一句 + actual= GetPolicy.MakeRequest(actual); + //System.Diagnostics.Process.Start(actual); + PrintLn(actual); + Assert.IsTrue(!string.IsNullOrEmpty(actual), "TextWaterMarkerTest MakeRequestTest Failure"); + } + } +} diff --git a/Qiniu.Test/IO/IOClientTest.cs b/Qiniu.Test/IO/IOClientTest.cs new file mode 100644 index 00000000..18a63887 --- /dev/null +++ b/Qiniu.Test/IO/IOClientTest.cs @@ -0,0 +1,110 @@ +using System; +using NUnit.Framework; +using Qiniu.IO; +using Qiniu.RS; +using Qiniu.Util; +using Qiniu.Test.TestHelper; + +namespace Qiniu.Test.IO +{ + [TestFixture] + public class IOClientTest:QiniuTestBase + { + + /// + ///PutFile 的测试 + /// + [Test] + public void PutFileTest() + { + + IOClient target = new IOClient(); + string key = NewKey; + PrintLn (key); + PutExtra extra = new PutExtra (); // TODO: 初始化为适当的值 + extra.MimeType = "text/plain"; + extra.Crc32 = 123; + extra.CheckCrc = CheckCrcType.CHECK; + extra.Params = new System.Collections.Generic.Dictionary (); + extra.Scope = Bucket; + PutPolicy put = new PutPolicy (extra.Scope); + TmpFIle file = new TmpFIle (1024 * 10); + target.PutFinished += new EventHandler ((o,e) => { + file.Del (); + if (e.OK) { + RSHelper.RSDel (Bucket, file.FileName); + } + }); + + PutRet ret = target.PutFile (put.Token (), file.FileName, file.FileName, extra); + + //error params + //target.PutFile("error", "error", "error", null); + Assert.IsTrue (ret.OK, "PutFileTest Failure"); + + } + /// + ///PutFile 的测试 + /// + [Test] + public void PutFileWithoutKeyTest() + { + + IOClient target = new IOClient(); + string key = NewKey; + PrintLn (key); + PutExtra extra = new PutExtra (); // TODO: 初始化为适当的值 + extra.MimeType = "text/plain"; + extra.Crc32 = 123; + extra.CheckCrc = CheckCrcType.CHECK; + extra.Params = new System.Collections.Generic.Dictionary (); + extra.Scope = Bucket; + PutPolicy put = new PutPolicy (extra.Scope); + TmpFIle file = new TmpFIle (1024 * 10); + target.PutFinished += new EventHandler ((o,e) => { + file.Del (); + if (e.OK) { + RSHelper.RSDel (Bucket, file.FileName); + } + }); + + PutRet ret = target.PutFileWithoutKey (put.Token (),file.FileName, extra); + + //error params + //target.PutFile("error", "error", "error", null); + Assert.IsTrue (ret.OK, "PutFileTest Failure"); + + } + [Test] + public void PutTest() + { + IOClient target = new IOClient(); + string key = NewKey; + PrintLn(key); + PutExtra extra = new PutExtra(); // TODO: 初始化为适当的值 + extra.MimeType = "text/plain"; + extra.Crc32 = 123; + extra.CheckCrc = CheckCrcType.CHECK; + extra.Params = new System.Collections.Generic.Dictionary(); + extra.Scope = Bucket; + PutPolicy put = new PutPolicy(extra.Scope); + target.PutFinished += new EventHandler ((o,e) => { + if (e.OK) { + RSHelper.RSDel (Bucket, key); + } + }); + string token = put.Token (); + PutRet ret = target.Put(put.Token(), key, "Hello, Qiniu Cloud!".ToStream(), extra); + + Assert.IsTrue(ret.OK, "PutFileTest Failure"); + + } + [Test] + public void PutWithoutKeyTest() + { + + + } + } +} + diff --git a/Qiniu.Test/IO/Resumable/ResumablePutTest.cs b/Qiniu.Test/IO/Resumable/ResumablePutTest.cs new file mode 100644 index 00000000..aa771be2 --- /dev/null +++ b/Qiniu.Test/IO/Resumable/ResumablePutTest.cs @@ -0,0 +1,71 @@ +using System; +using System.IO; +using NUnit.Framework; +using Qiniu.IO.Resumable; +using Qiniu.RS; +using Qiniu.RPC; +using Qiniu.Util; +using Qiniu.Test.TestHelper; + +namespace Qiniu.Test.IO.Resumable +{ + /// + ///这是 ResumablePutTest 的测试类,旨在 + ///包含所有 ResumablePutTest 单元测试 + /// + [TestFixture()] + public class ResumablePutTest:QiniuTestBase + { + /// + ///PutFile 的测试 + /// + [Test] + public void ResumablePutFileTest() + { + Settings putSetting = new Settings(); // TODO: 初始化为适当的值 + string key=NewKey; + ResumablePutExtra extra = new ResumablePutExtra(); + extra.Notify += new EventHandler(extra_Notify); + extra.NotifyErr += new EventHandler(extra_NotifyErr); + extra.Bucket = Bucket; + ResumablePut target = new ResumablePut(putSetting, extra); // TODO: 初始化为适当的值 + Console.WriteLine ("extra.Bucket:"+extra.Bucket); + string upToken = new PutPolicy(extra.Bucket).Token(new Qiniu.Auth.digest.Mac()); + target.Progress += new Action(target_Progress); + TmpFIle file=new TmpFIle(1024*1024*4); + target.PutFinished += new EventHandler ((o,e) => { + file.Del (); + if (e.OK) { + RSHelper.RSDel (Bucket, key); + } + }); + target.PutFile (upToken, file.FileName, key); + + //Action a = new Action (() => + //{ + // target.PutFile (upToken, BigFile, NewKey); + //}); + //a.BeginInvoke (null,null); + + } + + void extra_NotifyErr(object sender, PutNotifyErrorEvent e) + { + + } + + void extra_Notify(object sender, PutNotifyEvent e) + { + PrintLn(e.BlkIdx.ToString()); + PrintLn(e.BlkSize.ToString()); + PrintLn(e.Ret.offset.ToString()); + } + void target_Progress(float obj) + { + if (obj > 0.999999) + { + PrintLn((obj * 100).ToString() + "%"); + } + } + } +} diff --git a/Qiniu.Test/Properties/AssemblyInfo.cs b/Qiniu.Test/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..73ca4d61 --- /dev/null +++ b/Qiniu.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("Qiniu.Test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Qiniu")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("6.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/Qiniu.Test/Qiniu.Test.csproj b/Qiniu.Test/Qiniu.Test.csproj new file mode 100644 index 00000000..e0237a85 --- /dev/null +++ b/Qiniu.Test/Qiniu.Test.csproj @@ -0,0 +1,72 @@ + + + + Debug + AnyCPU + 10.0.0 + 2.0 + {95DC2A77-2344-4315-9F6F-334CC928459C} + Library + Qiniu.Test + Qiniu.Test + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + false + + + full + true + bin\Release + prompt + 4 + false + + + + + ..\tools\nunit.framework.dll + + + + + + + + + + + + + + + + + + + + + + + + {AD4EA9D1-11C2-4BF6-8A06-72A966BC1B80} + Qiniu + False + + + + + + + + + + + + diff --git a/Qiniu.Test/QiniuTestBase.cs b/Qiniu.Test/QiniuTestBase.cs new file mode 100644 index 00000000..7ed56017 --- /dev/null +++ b/Qiniu.Test/QiniuTestBase.cs @@ -0,0 +1,45 @@ +using System; +using Qiniu.Conf; +using System.Collections; + +namespace Qiniu.Test +{ + public class QiniuTestBase + { + + protected static string Bucket = ""; + protected static string LocalKey = "gogopher.jpg"; + protected static string DOMAIN = "qiniuphotos.qiniudn.com"; + protected static string BigFile = @""; + protected static string FileOpUrl = "http://qiniuphotos.qiniudn.com/gogopher.jpg"; + protected static string NewKey + { + get { return Guid.NewGuid().ToString(); } + } + private static bool init = false; + private void Init() + { + if (init) + return; + + //for make test + + Config.ACCESS_KEY = System.Environment.GetEnvironmentVariable ("QINIU_ACCESS_KEY"); + Config.SECRET_KEY = System.Environment.GetEnvironmentVariable ("QINIU_SECRET_KEY"); + Bucket =System.Environment.GetEnvironmentVariable ("QINIU_TEST_BUCKET"); + DOMAIN =System.Environment.GetEnvironmentVariable ("QINIU_TEST_DOMAIN"); + + init = true; + } + + public QiniuTestBase() + { + Init(); + } + protected void PrintLn(string str) + { + Console.WriteLine(str); + } + } +} + diff --git a/Qiniu.Test/RS/RSClientTest.cs b/Qiniu.Test/RS/RSClientTest.cs new file mode 100644 index 00000000..48090fae --- /dev/null +++ b/Qiniu.Test/RS/RSClientTest.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using Qiniu.RS; +using Qiniu.RPC; + +namespace Qiniu.Test.RS +{ + /// + ///这是 RSClientTest 的测试类,旨在 + ///包含所有 RSClientTest 单元测试 + /// + [TestFixture] + public class RSClientTest:QiniuTestBase + { + private List tmpKeys=new List(); + + + [TestFixtureSetUp] + public void BeforeTest() + { + #region before test + tmpKeys = RSHelper.RSPut(Bucket,4); + #endregion + } + [TestFixtureTearDown] + public void AfterTest() + { + foreach (string k in tmpKeys) { + RSHelper.RSDel (Bucket, k); + } + } + /// + ///Stat 的测试 + /// + [Test] + public void StatTest() + { + RSClient target = new RSClient(); + //YES + EntryPath scope = new EntryPath(Bucket, tmpKeys[0]); + Entry actual; + actual = target.Stat(scope); + Assert.IsTrue(actual.OK, "StatTest Failure"); + } + + /// + ///Move 的测试 + /// + [Test] + public void MoveTest() + { + + RSClient target = new RSClient(); // TODO: 初始化为适当的值 + string key = NewKey; + EntryPathPair pathPair = new EntryPathPair(Bucket, tmpKeys[0], key); ; // TODO: 初始化为适当的值 + CallRet actual; + //YES + actual = target.Move(pathPair); + if (actual.OK) { + tmpKeys [0] = key; + } + Assert.IsTrue(actual.OK, "MoveTest Failure"); + } + + /// + ///Delete 的测试 + /// + //[Test] + //public void DeleteTest() + //{ + /* + RSClient target = new RSClient(); // TODO: 初始化为适当的值 + EntryPath scope = new EntryPath(Bucket,LocalKey); // TODO: 初始化为适当的值 + CallRet actual; + actual = target.Delete(scope); + Assert.IsTrue(actual.OK, "DeleteTest Failure"); + */ + //} + + /// + ///Copy 的测试 + /// + [Test] + public void CopyTest() + { + RSClient target = new RSClient(); // TODO: 初始化为适当的值 + string key = NewKey; + EntryPathPair pathPair = new EntryPathPair(Bucket, tmpKeys[0], key); // TODO: 初始化为适当的值 + CallRet actual; + actual = target.Copy(pathPair); + if (actual.OK) { + RSHelper.RSDel (Bucket, key); + } + Assert.IsTrue(actual.OK, "CopyTest Failure"); + } + + /// + ///BatchStat 的测试 + /// + [Test] + public void BatchStatTest() + { + RSClient target = new RSClient(); // TODO: 初始化为适当的值 + EntryPath[] keys = new EntryPath[2]; // TODO: 初始化为适当的值 + keys[0] = new EntryPath(Bucket, tmpKeys[0]); + keys[1] = new EntryPath(Bucket, tmpKeys[1]);//error params + List actual; + actual = target.BatchStat(keys); + Assert.IsTrue(actual.Count == 2, "BatchStatTest Failure"); + } + + /// + ///BatchMove 的测试 + /// + [Test] + public void BatchMoveTest() + { + RSClient target = new RSClient(); // TODO: 初始化为适当的值 + EntryPathPair[] entryPathPairs = new EntryPathPair[2]; // TODO: 初始化为适当的值 + string tmpKey = NewKey; + string tmpKey2 = NewKey; + entryPathPairs[0] = new EntryPathPair(Bucket, tmpKeys[0], tmpKey); + entryPathPairs[1] = new EntryPathPair(Bucket, tmpKeys[1], tmpKey2); + + CallRet actual; + actual = target.BatchMove(entryPathPairs); + if (actual.OK) { + tmpKeys [0] = tmpKey; + tmpKeys [1] = tmpKey2; + } + Assert.IsTrue(actual.OK, "BatchMoveTest Failure"); + } + + /// + ///BatchDelete 的测试 + /// + [Test] + public void BatchDeleteTest() + { + List tmps = RSHelper.RSPut(Bucket,2); + + RSClient target = new RSClient(); // TODO: 初始化为适当的值 + EntryPath[] keys = new EntryPath[2]; // TODO: 初始化为适当的值 + int i = 0; + foreach (string k in tmps) { + keys [i++] = new EntryPath (Bucket, k); + } + + CallRet actual; + actual = target.BatchDelete(keys); + if (actual.OK) { + foreach (string k in tmps) { + RSHelper.RSDel(Bucket,k); + } + } + Assert.IsTrue(actual.OK, "BatchStatTest Failure"); ; + } + + /// + ///BatchCopy 的测试 + /// + [Test] + public void BatchCopyTest() + { + RSClient target = new RSClient(); // TODO: 初始化为适当的值 + + EntryPathPair[] entryPathPairs = new EntryPathPair[2]; // TODO: 初始化为适当的值 + string tmpKey = NewKey; + string tmpKey2 = NewKey; + entryPathPairs[0] = new EntryPathPair(Bucket, tmpKeys[0], tmpKey); + entryPathPairs[1] = new EntryPathPair(Bucket, tmpKeys[1], tmpKey2); + CallRet actual; + actual = target.BatchCopy(entryPathPairs); + if (actual.OK) { + RSHelper.RSDel (Bucket, tmpKey); + RSHelper.RSDel (Bucket, tmpKey2); + } + Assert.IsTrue(actual.OK, "BatchStatTest Failure"); ; + } + } +} diff --git a/Qiniu.Test/RSF/RSFClientTest.cs b/Qiniu.Test/RSF/RSFClientTest.cs new file mode 100644 index 00000000..f1ea12e0 --- /dev/null +++ b/Qiniu.Test/RSF/RSFClientTest.cs @@ -0,0 +1,84 @@ +using System; +using System.Collections.Generic; +using NUnit.Framework; +using Qiniu.RSF; +using Qiniu.Conf; +using Qiniu.Test.TestHelper; + +namespace Qiniu.Test.RSF +{ + /// + ///这是 RSFClientTest 的测试类,旨在 + ///包含所有 RSFClientTest 单元测试 + /// + [TestFixture] + public class RSFClientTest:QiniuTestBase + { + private List tmpKeys = new List (); + + public RSFClientTest () + { + + } + + [TestFixtureSetUp] + public void BeforeTest () + { + #region before test + tmpKeys = RSHelper.RSPut (Bucket, 3); + #endregion + } + + [TestFixtureTearDown] + public void AfterTest () + { + foreach (string k in tmpKeys) { + RSHelper.RSDel (Bucket, k); + } + } + /* + /// + ///Next 的测试 + /// + [Test] + public void NextTest() + { + RSFClient target = new RSFClient(Bucket); // TODO: 初始化为适当的值 + target.Init(); + target.Marker = string.Empty; + target.Prefix = string.Empty; + target.Limit = 1000; + List actual; + int count = 0; + actual = target.Next(); + while (actual != null) + { + count += actual.Count; + actual = target.Next(); + } + Assert.IsTrue(count >= 3, "ListPrefixTest Failure"); + } + */ + /// + ///ListPrefix 的测试 + /// + [Test] + public void ListPrefixTest () + { + RSFClient target = new RSFClient (Bucket); // TODO: 初始化为适当的值 + target.Marker = string.Empty; + target.Prefix = "csharp"; + target.Limit = 3; + DumpRet actual; + actual = target.ListPrefix (Bucket); + foreach (DumpItem item in actual.Items) { + Console.WriteLine ("Key:{0},Hash:{1},Mime:{2},PutTime:{3},EndUser:{4}", item.Key, item.Hash, item.Mime, item.PutTime, item.EndUser); + } + + //error params + Assert.IsTrue (actual.Items.Count >= 3, "ListPrefixTest Failure"); + + } + } +} + diff --git a/Qiniu.Test/TestHelper/IOHelper.cs b/Qiniu.Test/TestHelper/IOHelper.cs new file mode 100644 index 00000000..907436f5 --- /dev/null +++ b/Qiniu.Test/TestHelper/IOHelper.cs @@ -0,0 +1,38 @@ +using System; +using System.IO; + +namespace Qiniu.Test.TestHelper +{ + public class TmpFIle + { + public string FileName; + static string fileContent="Hello,Qiniu Cloud!"; + public TmpFIle(int fileSize) + { + FileName = string.Format ("./tmpFile{0}", Guid.NewGuid ().ToString ()); + using (FileStream fstream = new FileStream (FileName,FileMode.CreateNew)) + using (StreamWriter writer = new StreamWriter (fstream)) { + for (int i=0; i RSPut(string bucket,int num) + { + IOClient target = new IOClient(); + string key = "csharp" + Guid.NewGuid().ToString(); + //PrintLn(key); + PutExtra extra = new PutExtra(); // TODO: 初始化为适当的值 + extra.MimeType = "text/plain"; + extra.Scope = bucket; + PutPolicy put = new PutPolicy(extra.Scope); + + List newKeys=new List(); + for (int i=0; i + /// + /// + /// + /// + public override void SetAuth (HttpWebRequest request, Stream body) + { + string authHead = "UpToken " + UpToken; + request.Headers.Add ("Authorization", authHead); + } + } +} diff --git a/Qiniu/Auth/QiniuAuthClient.cs b/Qiniu/Auth/QiniuAuthClient.cs new file mode 100644 index 00000000..c67a7cf9 --- /dev/null +++ b/Qiniu/Auth/QiniuAuthClient.cs @@ -0,0 +1,63 @@ +using System; +using System.Text; +using System.Net; +using System.IO; +using System.Security.Cryptography; +using Qiniu.Util; +using Qiniu.RPC; +using Qiniu.Conf; +using Qiniu.Auth.digest; + +namespace Qiniu.Auth +{ + public class QiniuAuthClient : Client + { + protected Mac mac; + + public QiniuAuthClient (Mac mac = null) + { + this.mac = mac == null ? new Mac () : mac; + } + + private string SignRequest (System.Net.HttpWebRequest request, byte[] body) + { + Uri u = request.Address; + using (HMACSHA1 hmac = new HMACSHA1(this.mac.SecretKey)) { + string pathAndQuery = request.Address.PathAndQuery; + byte[] pathAndQueryBytes = Config.Encoding.GetBytes (pathAndQuery); + using (MemoryStream buffer = new MemoryStream()) { + buffer.Write (pathAndQueryBytes, 0, pathAndQueryBytes.Length); + buffer.WriteByte ((byte)'\n'); + if (body.Length > 0) { + buffer.Write (body, 0, body.Length); + } + byte[] digest = hmac.ComputeHash (buffer.ToArray ()); + string digestBase64 = Base64URLSafe.Encode (digest); + return mac.AccessKey + ":" + digestBase64; + } + } + } + + public override void SetAuth (HttpWebRequest request, Stream body) + { + string pathAndQuery = request.Address.PathAndQuery; + byte[] pathAndQueryBytes = Config.Encoding.GetBytes (pathAndQuery); + using (MemoryStream buffer = new MemoryStream()) { + string digestBase64 = null; + if (request.ContentType == "application/x-www-form-urlencoded" && body != null) { + if (!body.CanSeek) { + throw new Exception ("stream can not seek"); + } + Util.IO.Copy (buffer, body); + digestBase64 = SignRequest (request, buffer.ToArray()); + } else { + buffer.Write (pathAndQueryBytes, 0, pathAndQueryBytes.Length); + buffer.WriteByte ((byte)'\n'); + digestBase64 = mac.Sign (buffer.ToArray ()); + } + string authHead = "QBox " + digestBase64; + request.Headers.Add ("Authorization", authHead); + } + } + } +} diff --git a/Qiniu/Auth/digest/Mac.cs b/Qiniu/Auth/digest/Mac.cs new file mode 100644 index 00000000..710eeb6a --- /dev/null +++ b/Qiniu/Auth/digest/Mac.cs @@ -0,0 +1,106 @@ +using System; +using System.IO; +using System.Security.Cryptography; +using Qiniu.Conf; +using Qiniu.Util; + +namespace Qiniu.Auth.digest +{ + /// + /// 七牛消息认证(Message Authentication) + /// + public class Mac + { + + private string accessKey; + + /// + /// Gets or sets the access key. + /// + /// The access key. + public string AccessKey { + get { return accessKey; } + set { accessKey = value; } + } + + private byte[] secretKey; + + /// + /// Gets the secret key. + /// + /// The secret key. + public byte[] SecretKey { + get { return secretKey; } + } + + public Mac () + { + this.accessKey = Conf.Config.ACCESS_KEY; + this.secretKey = Config.Encoding.GetBytes (Config.SECRET_KEY); + } + + public Mac (string access, byte[] secretKey) + { + this.accessKey = access; + this.secretKey = secretKey; + } + + /// + /// + /// + /// + /// + private string _sign (byte[] data) + { + HMACSHA1 hmac = new HMACSHA1 (SecretKey); + byte[] digest = hmac.ComputeHash (data); + return Base64URLSafe.Encode (digest); + } + + /// + /// Sign + /// + /// + /// + public string Sign (byte[] b) + { + return string.Format ("{0}:{1}", this.accessKey, _sign (b)); + } + + /// + /// SignWithData + /// + /// + /// + public string SignWithData (byte[] b) + { + string data = Base64URLSafe.Encode (b); + return string.Format ("{0}:{1}:{2}", this.accessKey, _sign (Config.Encoding.GetBytes (data)), data); + } + + /// + /// SignRequest + /// + /// + /// + /// + public string SignRequest (System.Net.HttpWebRequest request, byte[] body) + { + Uri u = request.Address; + using (HMACSHA1 hmac = new HMACSHA1(secretKey)) { + string pathAndQuery = request.Address.PathAndQuery; + byte[] pathAndQueryBytes = Config.Encoding.GetBytes (pathAndQuery); + using (MemoryStream buffer = new MemoryStream()) { + buffer.Write (pathAndQueryBytes, 0, pathAndQueryBytes.Length); + buffer.WriteByte ((byte)'\n'); + if (body.Length > 0) { + buffer.Write (body, 0, body.Length); + } + byte[] digest = hmac.ComputeHash (buffer.ToArray ()); + string digestBase64 = Base64URLSafe.Encode (digest); + return this.accessKey + ":" + digestBase64; + } + } + } + } +} \ No newline at end of file diff --git a/Qiniu/Conf/Config.cs b/Qiniu/Conf/Config.cs new file mode 100644 index 00000000..b1eaf4fa --- /dev/null +++ b/Qiniu/Conf/Config.cs @@ -0,0 +1,51 @@ +using System; +using System.Text; + +namespace Qiniu.Conf +{ + public class Config + { + public static string USER_AGENT = "qiniu csharp-sdk v6.0.0"; + #region 帐户信息 + /// + /// 七牛提供的公钥,用于识别用户 + /// + public static string ACCESS_KEY = ""; + /// + /// 七牛提供的秘钥,不要在客户端初始化该变量 + /// + public static string SECRET_KEY = ""; + #endregion + #region 七牛服务器地址 + /// + /// 七牛资源管理服务器地址 + /// + public static string RS_HOST = "http://rs.Qbox.me"; + /// + /// 七牛资源上传服务器地址. + /// + public static string UP_HOST = "http://up.qiniu.com"; + /// + /// 七牛资源列表服务器地址. + /// + public static string RSF_HOST = "http://rsf.Qbox.me"; + #endregion + /// + /// 七牛SDK对所有的字节编码采用utf-8形式 . + /// + public static Encoding Encoding = Encoding.UTF8; + + /// + /// 初始化七牛帐户、请求地址等信息,不应在客户端调用。 + /// + public static void Init () + { + USER_AGENT = System.Configuration.ConfigurationManager.AppSettings ["USER_AGENT"]; + ACCESS_KEY = System.Configuration.ConfigurationManager.AppSettings ["ACCESS_KEY"]; + SECRET_KEY = System.Configuration.ConfigurationManager.AppSettings ["SECRET_KEY"]; + RS_HOST = System.Configuration.ConfigurationManager.AppSettings ["RS_HOST"]; + UP_HOST = System.Configuration.ConfigurationManager.AppSettings ["UP_HOST"]; + RSF_HOST = System.Configuration.ConfigurationManager.AppSettings ["RSF_HOST"]; + } + } +} diff --git a/Qiniu/FileOp/Exif.cs b/Qiniu/FileOp/Exif.cs new file mode 100644 index 00000000..a729fe1c --- /dev/null +++ b/Qiniu/FileOp/Exif.cs @@ -0,0 +1,19 @@ +using System; +using Qiniu.RPC; + +namespace Qiniu.FileOp +{ + public static class Exif + { + public static string MakeRequest (string url) + { + return url + "?exif"; + } + + public static ExifRet Call (string url) + { + CallRet callRet = FileOpClient.Get (url); + return new ExifRet (callRet); + } + } +} diff --git a/Qiniu/FileOp/ExifRet.cs b/Qiniu/FileOp/ExifRet.cs new file mode 100644 index 00000000..6be65ed7 --- /dev/null +++ b/Qiniu/FileOp/ExifRet.cs @@ -0,0 +1,52 @@ +using System; +using System.Collections.Generic; +using Qiniu.RPC; +using Newtonsoft.Json; + +namespace Qiniu.FileOp +{ + public class ExifValType + { + public string val { get; set; } + + public int type { get; set; } + } + + public class ExifRet : CallRet + { + private Dictionary dict; + + public ExifValType this [string key] { + get { + return dict [key]; + } + } + + public ExifRet (CallRet ret) + : base(ret) + { + if (!String.IsNullOrEmpty (Response)) { + try { + Unmarshal (Response); + } catch (Exception e) { + Console.WriteLine (e.ToString ()); + this.Exception = e; + } + } + } + + private void Unmarshal (string json) + { + dict = JsonConvert.DeserializeObject> (json); + } + + public override string ToString () + { + try { + return JsonConvert.SerializeObject (dict).ToString (); + } catch { + return string.Empty; + } + } + } +} diff --git a/Qiniu/FileOp/FileOpClient.cs b/Qiniu/FileOp/FileOpClient.cs new file mode 100644 index 00000000..8982eab8 --- /dev/null +++ b/Qiniu/FileOp/FileOpClient.cs @@ -0,0 +1,34 @@ +using System; +using System.Net; +using System.IO; +using Qiniu.RPC; + +namespace Qiniu.FileOp +{ + static class FileOpClient + { + public static CallRet Get (string url) + { + try { + HttpWebRequest request = (HttpWebRequest)WebRequest.Create (url); + request.Method = "GET"; + request.UserAgent = Conf.Config.USER_AGENT; + using (HttpWebResponse response = request.GetResponse() as HttpWebResponse) { + return HandleResult (response); + } + } catch (Exception e) { + Console.WriteLine (e.ToString ()); + return new CallRet (HttpStatusCode.BadRequest, e); + } + } + + public static CallRet HandleResult (HttpWebResponse response) + { + HttpStatusCode statusCode = response.StatusCode; + using (StreamReader reader = new StreamReader(response.GetResponseStream())) { + string responseStr = reader.ReadToEnd (); + return new CallRet (statusCode, responseStr); + } + } + } +} diff --git a/Qiniu/FileOp/ImageInfo.cs b/Qiniu/FileOp/ImageInfo.cs new file mode 100644 index 00000000..35b0e798 --- /dev/null +++ b/Qiniu/FileOp/ImageInfo.cs @@ -0,0 +1,19 @@ +using System; +using Qiniu.RPC; + +namespace Qiniu.FileOp +{ + public static class ImageInfo + { + public static string MakeRequest (string url) + { + return url + "?imageInfo"; + } + + public static ImageInfoRet Call (string url) + { + CallRet callRet = FileOpClient.Get (url); + return new ImageInfoRet (callRet); + } + } +} diff --git a/Qiniu/FileOp/ImageInfoRet.cs b/Qiniu/FileOp/ImageInfoRet.cs new file mode 100644 index 00000000..97c5a6da --- /dev/null +++ b/Qiniu/FileOp/ImageInfoRet.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using Qiniu.RPC; +using Newtonsoft.Json; + +namespace Qiniu.FileOp +{ + public class ImageInfoRet : CallRet + { + public int Width { get; private set; } + + public int Height { get; private set; } + + public string Format { get; private set; } + + public string ColorModel { get; private set; } + + public ImageInfoRet (CallRet ret) + : base(ret) + { + if (!String.IsNullOrEmpty (Response)) { + try { + Unmarshal (Response); + } catch (Exception e) { + Console.WriteLine (e.ToString ()); + this.Exception = e; + } + } + } + + private void Unmarshal (string json) + { + var dics = JsonConvert.DeserializeObject> (json); + dynamic tmp; + if (dics.TryGetValue ("format", out tmp)) { + Format = (string)tmp; + } + if (dics.TryGetValue ("width", out tmp)) { + Width = (int)tmp; + } + if (dics.TryGetValue ("height", out tmp)) { + Height = (int)tmp; + } + if (dics.TryGetValue ("colorModel", out tmp)) { + ColorModel = (string)tmp; + } + } + } +} diff --git a/Qiniu/FileOp/ImageMogrify.cs b/Qiniu/FileOp/ImageMogrify.cs new file mode 100644 index 00000000..5c4690da --- /dev/null +++ b/Qiniu/FileOp/ImageMogrify.cs @@ -0,0 +1,44 @@ +using System; +using System.Runtime; +using System.Reflection; +using System.Text; + +namespace Qiniu.FileOp +{ + public class ImageMogrify + { + public bool AutoOrient { get; set; } + + public string Thumbnail { get; set; } + + public string Gravity { get; set; } + + public string Crop { get; set; } + + public int Quality { get; set; } + + public int Rotate { get; set; } + + public string Format { get; set; } + + public string MakeRequest (string url) + { + string spec = url + "?imageMogr"; + if (AutoOrient) + spec += "/auto-orient"; + if (!String.IsNullOrEmpty (Thumbnail)) + spec += "/thumbnail/" + Thumbnail; + if (!String.IsNullOrEmpty (Gravity)) + spec += "/gravity/" + Gravity; + if (!String.IsNullOrEmpty (Crop)) + spec += "/crop/" + Crop; + if (Quality != 0) + spec += "/quality/" + Quality.ToString (); + if (Rotate != 0) + spec += "/rotate/" + Rotate.ToString (); + if (!String.IsNullOrEmpty (Format)) + spec += "/format/" + Format; + return spec; + } + } +} diff --git a/Qiniu/FileOp/ImageView.cs b/Qiniu/FileOp/ImageView.cs new file mode 100644 index 00000000..c4b5e9e1 --- /dev/null +++ b/Qiniu/FileOp/ImageView.cs @@ -0,0 +1,51 @@ +using System; + +namespace Qiniu.FileOp +{ + public class ImageView + { + /// + /// 缩略模式 + /// + /// The mode. + public int Mode { get; set; } + /// + /// Width = 0 表示不限定宽度 + /// + /// The width. + public int Width { get; set; } + /// + /// Height = 0 表示不限定高度 + /// + /// The height. + public int Height { get; set; } + /// + ///质量, 1-100 + /// + /// The quality. + public int Quality { get; set; } + /// + /// 输出格式,如jpg, gif, png, tif等等 + /// + /// The format. + public string Format { get; set; } + /// + /// Makes the request. + /// + /// The request. + /// URL. + public string MakeRequest (string url) + { + string spec = url + "?imageView/" + Mode.ToString (); + if (Width != 0) + spec += "/w/" + Width.ToString (); + if (Height != 0) + spec += "/h/" + Height.ToString (); + if (Quality != 0) + spec += "/q/" + Quality.ToString (); + if (!String.IsNullOrEmpty (Format)) + spec += "/format/" + Format; + return spec; + } + } +} diff --git a/Qiniu/FileOp/ImageWaterMarker.cs b/Qiniu/FileOp/ImageWaterMarker.cs new file mode 100644 index 00000000..8d25520c --- /dev/null +++ b/Qiniu/FileOp/ImageWaterMarker.cs @@ -0,0 +1,32 @@ +using System; +using System.Text; +using Qiniu.Util; + +namespace Qiniu.FileOp +{ + public class ImageWaterMarker:WaterMarker + { + public string imageUrl; + + public ImageWaterMarker (string imageUrl, int dissolve=50, MarkerGravity gravity = MarkerGravity.SouthEast, int dx = 10, int dy = 10) + : base(dissolve,gravity, dx, dy) + { + this.imageUrl = imageUrl; + } + + public override string MakeRequest (string url) + { + StringBuilder sb = new StringBuilder (); + sb.Append (string.Format ("{0}?watermark/{1}", url, 1)); + if (string.IsNullOrEmpty (imageUrl)) { + throw new Exception ("Water Marker Image Url Error"); + } + sb.Append ("/image/" + imageUrl.ToBase64URLSafe ()); + sb.Append ("/dissolve/" + dissolve); + sb.Append ("/gravity/" + Gravitys [(int)gravity]); + sb.Append ("/dx/" + dx); + sb.Append ("/dy/" + dy); + return sb.ToString (); + } + } +} diff --git a/Qiniu/FileOp/TextWaterMarker.cs b/Qiniu/FileOp/TextWaterMarker.cs new file mode 100644 index 00000000..ca932cf3 --- /dev/null +++ b/Qiniu/FileOp/TextWaterMarker.cs @@ -0,0 +1,48 @@ +using System; +using System.Text; +using Qiniu.Util; + +namespace Qiniu.FileOp +{ + public class TextWaterMarker:WaterMarker + { + public string text; + private string fontName; + private int fontSize; + private string color; + + public TextWaterMarker (string text, string fontname = "", string color = "", int fontsize = 0, int dissolve = 50, MarkerGravity gravity = MarkerGravity.SouthEast, int dx = 10, int dy = 10) + : base(dissolve,gravity, dx, dy) + { + this.text = text; + this.fontName = fontname; + this.fontSize = fontsize; + this.color = color; + } + + public override string MakeRequest (string url) + { + StringBuilder sb = new StringBuilder (); + sb.Append (string.Format ("{0}?watermark/{1}", url, 2)); + if (string.IsNullOrEmpty (text)) { + throw new Exception ("No Text To Draw"); + } + sb.Append ("/text/" + text.ToBase64URLSafe ()); + + if (!string.IsNullOrEmpty (fontName)) { + sb.Append ("/font/" + fontName.ToBase64URLSafe ()); + } + if (fontSize > 0) { + sb.Append ("/fontsize/" + fontSize); + } + if (!string.IsNullOrEmpty (color)) { + sb.Append ("/fill/" + color.ToBase64URLSafe ()); + } + sb.Append ("/dissolve/" + dissolve); + sb.Append ("/gravity/" + Gravitys [(int)gravity]); + sb.Append ("/dx/" + dx); + sb.Append ("/dy/" + dy); + return sb.ToString (); + } + } +} diff --git a/Qiniu/FileOp/WaterMarker.cs b/Qiniu/FileOp/WaterMarker.cs new file mode 100644 index 00000000..02cf4012 --- /dev/null +++ b/Qiniu/FileOp/WaterMarker.cs @@ -0,0 +1,69 @@ +using Qiniu.RPC; + +namespace Qiniu.FileOp +{ + public enum MarkerGravity + { + NorthWest = 0, + North, + NorthEast, + West, + Center, + East, + SouthWest, + South, + SouthEast + } + + public class WaterMarker + { + protected static string[] Gravitys = new string[9] { + "NorthWest", + "North", + "NorthEast", + "West", + "Center", + "East", + "SouthWest", + "South", + "SouthEast" + }; + protected int dx; + protected int dy; + protected int dissolve; + + public int Dissolve { + get { return dissolve; } + set { + if (value < 0) + dissolve = 0; + else if (value > 100) + dissolve = 100; + else + dissolve = value; + } + } + + public MarkerGravity gravity; + + public WaterMarker (int dissolve = 50, MarkerGravity gravity = MarkerGravity.SouthEast, int dx = 10, int dy = 10) + { + Dissolve = dissolve; + this.dissolve = dissolve; + this.dx = dx; + this.dy = dy; + this.gravity = gravity; + } + + public virtual string MakeRequest (string url) + { + return null; + } + + public static ExifRet Call (string url) + { + CallRet callRet = FileOpClient.Get (url); + return new ExifRet (callRet); + } + } +} diff --git a/Qiniu/IO/FileParameter.cs b/Qiniu/IO/FileParameter.cs new file mode 100644 index 00000000..bd67d90b --- /dev/null +++ b/Qiniu/IO/FileParameter.cs @@ -0,0 +1,69 @@ +using System; +using System.IO; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Qiniu.IO +{ + class PutParameter + { + protected string mimeType; + + public string MimeType + { + get { return mimeType; } + set { mimeType = value; } + } + public PutParameter(string mimeType) + { + this.mimeType = mimeType; + } + public virtual long CopyTo(Stream body) + { + return 0; + } + } + class StreamParameter:PutParameter + { + private System.IO.StreamReader reader; + + public System.IO.StreamReader Reader + { + get { return reader; } + set { reader = value; } + } + public StreamParameter(StreamReader reader, string mimeType):base(mimeType) + { + this.reader = reader; + } + public override long CopyTo(Stream body) + { + return 0; + } + } + class FileParameter : PutParameter + { + private string fileName; + + public string FileName + { + get { return fileName; } + set { fileName = value; } + } + + public FileParameter(string fname, string mimeType):base(mimeType) + { + this.fileName = fname; + } + + public override long CopyTo(Stream body) + { + using (FileStream fs = File.OpenRead(this.fileName)) + { + fs.CopyTo(body); + return fs.Length; + } + } + } +} diff --git a/Qiniu/IO/IOClient.cs b/Qiniu/IO/IOClient.cs new file mode 100644 index 00000000..04601206 --- /dev/null +++ b/Qiniu/IO/IOClient.cs @@ -0,0 +1,122 @@ +using System; +using System.Collections.Generic; +using System.Net; +using Qiniu.Conf; +using Qiniu.Auth; +using Qiniu.RPC; +using Qiniu.Util; +using System.Collections.Specialized; + +namespace Qiniu.IO +{ + /// + /// 上传客户端 + /// + public class IOClient + { + /// + /// 无论成功或失败,上传结束时触发的事件 + /// + public event EventHandler PutFinished; + + private static NameValueCollection getFormData(string upToken, string key, PutExtra extra) + { + NameValueCollection formData = new NameValueCollection(); + formData["token"] = upToken; + formData["key"] = key; + if (extra != null && extra.Params != null) + { + if (extra.CheckCrc == CheckCrcType.CHECK_AUTO) + { + formData["crc32"] = extra.Crc32.ToString(); + } + foreach (KeyValuePair pair in extra.Params) + { + formData[pair.Key] = pair.Value; + } + } + return formData; + } + + + /// + /// 上传文件 + /// + /// + /// h + /// + /// + public PutRet PutFile(string upToken, string key, string localFile, PutExtra extra) + { + if (!System.IO.File.Exists (localFile)) { + throw new Exception (string.Format ("{0} does not exist", localFile)); + } + PutRet ret; + + NameValueCollection formData = getFormData(upToken, key, extra); + try + { + CallRet callRet = MultiPart.MultiPost(Config.UP_HOST, formData, localFile); + ret = new PutRet(callRet); + onPutFinished(ret); + return ret; + } + catch (Exception e) + { + ret = new PutRet(new CallRet(HttpStatusCode.BadRequest, e)); + onPutFinished(ret); + return ret; + } + } + /// + /// Puts the file without key. + /// + /// The file without key. + /// Up token. + /// Local file. + /// Extra. + public PutRet PutFileWithoutKey(string upToken,string localFile,PutExtra extra) + { + return PutFile (upToken, string.Empty, localFile, extra); + + } + + /// + /// + /// + /// Up token. + /// Key. + /// Put stream. + /// Extra. + public PutRet Put(string upToken, string key, System.IO.Stream putStream, PutExtra extra) + { + if (!putStream.CanRead) { + throw new Exception ("read put Stream error"); + } + PutRet ret; + NameValueCollection formData = getFormData(upToken, key, extra); + try + { + + CallRet callRet = MultiPart.MultiPost(Config.UP_HOST, formData, putStream); + ret = new PutRet(callRet); + onPutFinished(ret); + return ret; + } + catch (Exception e) + { + ret = new PutRet(new CallRet(HttpStatusCode.BadRequest, e)); + onPutFinished(ret); + return ret; + } + } + + protected void onPutFinished(PutRet ret) + { + if (PutFinished != null) + { + PutFinished(this, ret); + } + } + } +} diff --git a/Qiniu/IO/MultiPart.cs b/Qiniu/IO/MultiPart.cs new file mode 100644 index 00000000..5eae87a7 --- /dev/null +++ b/Qiniu/IO/MultiPart.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Specialized; +using System.Collections.Generic; +using System.IO; +using System.Net; +using System.Text; +using Qiniu.Conf; +using Qiniu.RPC; + +namespace Qiniu.IO +{ + static class MultiPart + { + public static Encoding encoding = Config.Encoding; + + public static string RandomBoundary () + { + return String.Format ("----------{0:N}", Guid.NewGuid ()); + } + + public static string FormDataContentType (string boundary) + { + return "multipart/form-data; boundary=" + boundary; + } + + private static Stream GetPostStream (Stream putStream, string fileName, NameValueCollection formData, string boundary) + { + Stream postDataStream = new System.IO.MemoryStream (); + + //adding form data + + string formDataHeaderTemplate = Environment.NewLine + "--" + boundary + Environment.NewLine + + "Content-Disposition: form-data; name=\"{0}\";" + Environment.NewLine + Environment.NewLine + "{1}"; + + foreach (string key in formData.Keys) { + byte[] formItemBytes = System.Text.Encoding.UTF8.GetBytes (string.Format (formDataHeaderTemplate, + key, formData [key])); + postDataStream.Write (formItemBytes, 0, formItemBytes.Length); + } + + //adding file,Stream data + #region adding file data + + string fileHeaderTemplate = Environment.NewLine + "--" + boundary + Environment.NewLine + + "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"" + + Environment.NewLine + "Content-Type: application/octet-stream" + Environment.NewLine + Environment.NewLine; + byte[] fileHeaderBytes = System.Text.Encoding.UTF8.GetBytes (string.Format (fileHeaderTemplate, + "file", fileName)); + postDataStream.Write (fileHeaderBytes, 0, fileHeaderBytes.Length); + + byte[] buffer = new byte[1024]; + int bytesRead = 0; + while ((bytesRead = putStream.Read(buffer, 0, buffer.Length)) != 0) { + postDataStream.Write (buffer, 0, bytesRead); + } + putStream.Close (); + #endregion + + #region adding end + byte[] endBoundaryBytes = System.Text.Encoding.UTF8.GetBytes (Environment.NewLine + "--" + boundary + "--" + Environment.NewLine); + postDataStream.Write (endBoundaryBytes, 0, endBoundaryBytes.Length); + #endregion + + return postDataStream; + + } + + private static Stream GetPostStream (string filePath, NameValueCollection formData, string boundary) + { + Stream postDataStream = new System.IO.MemoryStream (); + + //adding form data + + string formDataHeaderTemplate = Environment.NewLine + "--" + boundary + Environment.NewLine + + "Content-Disposition: form-data; name=\"{0}\";" + Environment.NewLine + Environment.NewLine + "{1}"; + + foreach (string key in formData.Keys) { + byte[] formItemBytes = System.Text.Encoding.UTF8.GetBytes (string.Format (formDataHeaderTemplate, + key, formData [key])); + postDataStream.Write (formItemBytes, 0, formItemBytes.Length); + } + + //adding file data + #region adding file data + FileInfo fileInfo = new FileInfo (filePath); + string fileHeaderTemplate = Environment.NewLine + "--" + boundary + Environment.NewLine + + "Content-Disposition: form-data; name=\"{0}\"; filename=\"{1}\"" + + Environment.NewLine + "Content-Type: application/octet-stream" + Environment.NewLine + Environment.NewLine; + byte[] fileHeaderBytes = System.Text.Encoding.UTF8.GetBytes (string.Format (fileHeaderTemplate, + "file", fileInfo.FullName)); + postDataStream.Write (fileHeaderBytes, 0, fileHeaderBytes.Length); + FileStream fileStream = fileInfo.OpenRead (); + byte[] buffer = new byte[1024]; + int bytesRead = 0; + while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0) { + postDataStream.Write (buffer, 0, bytesRead); + } + fileStream.Close (); + #endregion + + #region adding end + byte[] endBoundaryBytes = System.Text.Encoding.UTF8.GetBytes (Environment.NewLine + "--" + boundary + "--" + Environment.NewLine); + postDataStream.Write (endBoundaryBytes, 0, endBoundaryBytes.Length); + #endregion + + return postDataStream; + } + + public static CallRet MultiPost (string url, NameValueCollection formData, string fileName) + { + string boundary = RandomBoundary (); + System.Net.WebRequest webRequest = System.Net.WebRequest.Create (url); + + webRequest.Method = "POST"; + webRequest.ContentType = "multipart/form-data; boundary=" + boundary; + FileInfo fileInfo = new FileInfo (fileName); + + using (FileStream fileStream = fileInfo.OpenRead()) { + + Stream postDataStream = GetPostStream (fileStream, fileName, formData, boundary); + webRequest.ContentLength = postDataStream.Length; + Stream reqStream = webRequest.GetRequestStream (); + postDataStream.Position = 0; + + byte[] buffer = new byte[1024]; + int bytesRead = 0; + + while ((bytesRead = postDataStream.Read(buffer, 0, buffer.Length)) != 0) { + reqStream.Write (buffer, 0, bytesRead); + } + postDataStream.Close (); + reqStream.Close (); + } + try { + using (HttpWebResponse response = webRequest.GetResponse() as HttpWebResponse) { + return RPC.Client.HandleResult (response); + } + + } catch (Exception e) { + Console.WriteLine (e.ToString ()); + return new CallRet (HttpStatusCode.BadRequest, e); + } + } + + public static CallRet MultiPost (string url, NameValueCollection formData, System.IO.Stream inputStream) + { + string boundary = RandomBoundary (); + System.Net.WebRequest webRequest = System.Net.WebRequest.Create (url); + + webRequest.Method = "POST"; + webRequest.ContentType = "multipart/form-data; boundary=" + boundary; + + Stream postDataStream = GetPostStream (inputStream, formData ["key"], formData, boundary); + webRequest.ContentLength = postDataStream.Length; + Stream reqStream = webRequest.GetRequestStream (); + postDataStream.Position = 0; + + byte[] buffer = new byte[1024]; + int bytesRead = 0; + + while ((bytesRead = postDataStream.Read(buffer, 0, buffer.Length)) != 0) { + reqStream.Write (buffer, 0, bytesRead); + } + postDataStream.Close (); + reqStream.Close (); + + try { + using (HttpWebResponse response = webRequest.GetResponse() as HttpWebResponse) { + return RPC.Client.HandleResult (response); + } + + } catch (Exception e) { + Console.WriteLine (e.ToString ()); + return new CallRet (HttpStatusCode.BadRequest, e); + } + } + } +} diff --git a/Qiniu/IO/PutExtra.cs b/Qiniu/IO/PutExtra.cs new file mode 100644 index 00000000..3fce439a --- /dev/null +++ b/Qiniu/IO/PutExtra.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; + +namespace Qiniu.IO +{ + public enum CheckCrcType + { + /// + /// default + /// + DEFAULT_CHECK = -1, + /// + /// 表示不进行 crc32 校验 + /// + NO_CHECK = 0, + /// + ///对于 Put 等同于 CheckCrc = 2;对于 PutFile 会自动计算 crc32 值 + /// + CHECK_AUTO = 1, + /// + /// 表示进行 crc32 校验,且 crc32 值就是PutExtra:Crc32 + /// + CHECK = 2 + } + + public class PutExtra + { + public Dictionary Params{ get; set; } + + public string MimeType { get; set; } + + public Int32 Crc32 { get; set; } + + public CheckCrcType CheckCrc { get; set; } + + public string Scope { get; set; } + + public PutExtra () + { + Crc32 = -1; + } + + public PutExtra (string bucket, string mimeType) + { + Scope = bucket; + MimeType = mimeType; + Crc32 = -1; + } + } +} diff --git a/Qiniu/IO/PutRet.cs b/Qiniu/IO/PutRet.cs new file mode 100644 index 00000000..5e5007f1 --- /dev/null +++ b/Qiniu/IO/PutRet.cs @@ -0,0 +1,48 @@ +using System; +using System.Collections.Generic; + +using Qiniu.RPC; +using Newtonsoft.Json; + +namespace Qiniu.IO +{ + public class PutRet : CallRet + { + /// + /// 如果 uptoken 没有指定 ReturnBody,那么返回值是标准的 PutRet 结构 + /// + public string Hash { get; private set; } + + /// + /// 如果传入的 key == UNDEFINED_KEY,则服务端返回 key + /// + public string key { get; private set; } + + public PutRet (CallRet ret) + : base(ret) + { + if (!String.IsNullOrEmpty (Response)) { + try { + Unmarshal (Response); + } catch (Exception e) { + Console.WriteLine (e.ToString ()); + this.Exception = e; + } + } + } + + private void Unmarshal (string json) + { + try { + var dict = JsonConvert.DeserializeObject> (json); + object tmp; + if (dict.TryGetValue ("hash", out tmp)) + Hash = (string)dict ["hash"]; + if (dict.TryGetValue ("key", out tmp)) + key = (string)dict ["key"]; + } catch (Exception e) { + throw e; + } + } + } +} diff --git a/Qiniu/IO/Resumable/ResumablePut.cs b/Qiniu/IO/Resumable/ResumablePut.cs new file mode 100644 index 00000000..4abe0cde --- /dev/null +++ b/Qiniu/IO/Resumable/ResumablePut.cs @@ -0,0 +1,227 @@ +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Qiniu.Auth; +using Qiniu.Conf; +using Qiniu.RPC; +using Qiniu.RS; +using Qiniu.Util; + +namespace Qiniu.IO.Resumable +{ + /// + /// 异步并行断点上传类 + /// + public class ResumablePut + { + private const int blockBits = 22; + private const int blockMashk = (1 << blockBits) - 1; + private static int BLOCKSIZE = 4 * 1024 * 1024; + private const string UNDEFINED_KEY = "?"; + #region 记录总文件大小,用于计算上传百分比 + private long fsize; + private float chunks; + private float uploadedChunks = 0; + #endregion + /// + /// 上传完成事件 + /// + public event EventHandler PutFinished; + /// + /// 进度提示事件 + /// + public event Action Progress; + + Settings putSetting; + + /// + /// 上传设置 + /// + public Settings PutSetting { + get { return putSetting; } + set { putSetting = value; } + } + + ResumablePutExtra extra; + + /// + /// PutExtra + /// + public ResumablePutExtra Extra { + get { return extra; } + set { extra = value; } + } + + /// + /// 断点续传类 + /// + /// + /// + public ResumablePut (Settings putSetting, ResumablePutExtra extra) + { + extra.chunkSize = putSetting.ChunkSize; + this.putSetting = putSetting; + this.extra = extra; + } + + /// + /// 上传文件 + /// + /// 上传Token + /// key + /// 本地文件名 + public void PutFile (string upToken, string localFile, string key) + { + PutAuthClient client = new PutAuthClient (upToken); + using (FileStream fs = File.OpenRead(localFile)) { + int block_cnt = block_count (fs.Length); + fsize = fs.Length; + chunks = fsize / extra.chunkSize + 1; + extra.Progresses = new BlkputRet[block_cnt]; + //并行上传 + Parallel.For (0, block_cnt, (i) => + { + int readLen = BLOCKSIZE; + if ((i + 1) * BLOCKSIZE > fsize) + readLen = (int)(fsize - i * BLOCKSIZE); + byte[] byteBuf = new byte[readLen]; + lock (fs) { + fs.Seek (i * BLOCKSIZE, SeekOrigin.Begin); + fs.Read (byteBuf, 0, readLen); + BlkputRet blkRet = ResumableBlockPut (client, byteBuf, i, readLen); + if (blkRet == null) { + extra.OnNotifyErr (new PutNotifyErrorEvent (i, readLen, "Make Block Error")); + } else { + extra.OnNotify (new PutNotifyEvent (i, readLen, extra.Progresses [i])); + } + } + }); + if (string.IsNullOrEmpty (key)) { + key = UNDEFINED_KEY; + } + CallRet ret = Mkfile (client, key, fs.Length); + if (Progress != null) { + Progress (1.0f); + } + if (PutFinished != null) { + PutFinished (this, ret); + } + + } + + } + + /// + /// 百分比进度提示 + /// + private void progress () + { + uploadedChunks++; + if (Progress != null) { + Progress ((float)uploadedChunks / chunks); + } + } + + private BlkputRet ResumableBlockPut (Client client, byte[] body, int blkIdex, int blkSize) + { + int bodyLength; + int chunkSize = extra.chunkSize; + #region Mkblock + if (extra.Progresses [blkIdex] == null) { + bodyLength = chunkSize < blkSize ? chunkSize : blkSize; + byte[] firstChunk = new byte[bodyLength]; + Array.Copy (body, 0, firstChunk, 0, bodyLength); + uint crc32 = CRC32.CheckSumBytes (firstChunk); + for (int i = 0; i < putSetting.TryTimes; i++) { + extra.Progresses [blkIdex] = Mkblock (client, firstChunk, body.Length); + if (extra.Progresses [blkIdex] == null || crc32 != extra.Progresses [blkIdex].crc32) { + if (i == (putSetting.TryTimes - 1)) { + return null; + } + continue; + } else { + progress (); + break; + } + } + } + #endregion + #region PutBlock + while (extra.Progresses[blkIdex].offset < blkSize) { + bodyLength = (chunkSize < (blkSize - extra.Progresses [blkIdex].offset)) ? chunkSize : (int)(blkSize - extra.Progresses [blkIdex].offset); + byte[] chunk = new byte[bodyLength]; + Array.Copy (body, extra.Progresses [blkIdex].offset, chunk, 0, bodyLength); + for (int i = 0; i < putSetting.TryTimes; i++) { + extra.Progresses [blkIdex] = BlockPut (client, extra.Progresses [blkIdex], new MemoryStream (chunk), bodyLength); + if (extra.Progresses [blkIdex] == null) { + if (i == (putSetting.TryTimes - 1)) { + return null; + } + continue; + } else { + uploadedChunks++; + if (Progress != null) { + Progress ((float)uploadedChunks / chunks); + } + break; + } + } + } + #endregion + return extra.Progresses [blkIdex]; + } + + private BlkputRet Mkblock (Client client, byte[] firstChunk, long blkSize) + { + string url = string.Format ("{0}/mkblk/{1}", Config.UP_HOST, blkSize); + CallRet callRet = client.CallWithBinary (url, "application/octet-stream", new MemoryStream (firstChunk), firstChunk.Length); + if (callRet.OK) { + return callRet.Response.ToObject (); + } + return null; + } + + private BlkputRet BlockPut (Client client, BlkputRet ret, Stream body, long length) + { + string url = string.Format ("{0}/bput/{1}/{2}", Config.UP_HOST, ret.ctx, ret.offset); + CallRet callRet = client.CallWithBinary (url, "application/octet-stream", body, length); + if (callRet.OK) { + return callRet.Response.ToObject (); + } + return null; + } + + private CallRet Mkfile (Client client, string key, long fsize) + { + EntryPath entry = new EntryPath (extra.Bucket, key); + string url = string.Format ("{0}/rs-mkfile/{1}/fsize/{2}/", Config.UP_HOST, entry.Base64EncodedURI, fsize); + if (!string.IsNullOrEmpty (extra.MimeType)) { + url += (string.Format ("/mimeType/{0}", extra.MimeType.ToBase64URLSafe ())); + } + if (!string.IsNullOrEmpty (extra.CustomMeta)) { + url += (string.Format ("/meta/{0}", extra.CustomMeta.ToBase64URLSafe ())); + } + if (!string.IsNullOrEmpty (extra.CallbackParams)) { + url += (string.Format ("/params/{0}", extra.CallbackParams.ToBase64URLSafe ())); + } + int proCount = extra.Progresses.Length; + using (Stream body = new MemoryStream()) { + for (int i = 0; i < proCount; i++) { + byte[] bctx = Encoding.ASCII.GetBytes (extra.Progresses [i].ctx); + body.Write (bctx, 0, bctx.Length); + if (i != proCount - 1) { + body.WriteByte ((byte)','); + } + } + body.Seek (0, SeekOrigin.Begin); + return client.CallWithBinary (url, "text/plain", body, body.Length); + } + } + + private int block_count (long fsize) + { + return (int)((fsize + blockMashk) >> blockBits); + } + } +} diff --git a/Qiniu/IO/Resumable/ResumablePutExtra.cs b/Qiniu/IO/Resumable/ResumablePutExtra.cs new file mode 100644 index 00000000..78506b6d --- /dev/null +++ b/Qiniu/IO/Resumable/ResumablePutExtra.cs @@ -0,0 +1,95 @@ +using System; + +namespace Qiniu.IO.Resumable +{ + /// + /// Block上传成功事件参数 + /// + public class PutNotifyEvent:EventArgs + { + int blkIdx; + + public int BlkIdx { + get { return blkIdx; } + } + + int blkSize; + + public int BlkSize { + get { return blkSize; } + } + + BlkputRet ret; + + public BlkputRet Ret { + get { return ret; } + } + + public PutNotifyEvent (int blkIdx, int blkSize, BlkputRet ret) + { + this.blkIdx = blkIdx; + this.blkSize = blkSize; + this.ret = ret; + } + } + + /// + /// 上传错误事件参数 + /// + public class PutNotifyErrorEvent:EventArgs + { + int blkIdx; + + public int BlkIdx { + get { return blkIdx; } + } + + int blkSize; + + public int BlkSize { + get { return blkSize; } + } + + string error; + + public string Error { + get { return error; } + } + + public PutNotifyErrorEvent (int blkIdx, int blkSize, string error) + { + this.blkIdx = blkIdx; + this.blkSize = blkSize; + this.error = error; + } + } + + /// + /// + /// + public class ResumablePutExtra + { + public string CallbackParams; + public string Bucket; + public string CustomMeta; + public string MimeType; + public int chunkSize; + public int tryTimes; + public BlkputRet[] Progresses; + + public event EventHandler Notify; + public event EventHandler NotifyErr; + + public void OnNotify (PutNotifyEvent arg) + { + if (Notify != null) + Notify (this, arg); + } + + public void OnNotifyErr (PutNotifyErrorEvent arg) + { + if (NotifyErr != null) + NotifyErr (this, arg); + } + } +} diff --git a/Qiniu/IO/Resumable/ResumablePutRet.cs b/Qiniu/IO/Resumable/ResumablePutRet.cs new file mode 100644 index 00000000..e7dfa6d0 --- /dev/null +++ b/Qiniu/IO/Resumable/ResumablePutRet.cs @@ -0,0 +1,18 @@ +using System; +using Newtonsoft.Json; + +namespace Qiniu.IO.Resumable +{ + [JsonObject(MemberSerialization.OptIn)] + public class BlkputRet + { + [JsonProperty("ctx")] + public string ctx; + [JsonProperty("checksum")] + public string checkSum; + [JsonProperty("crc32")] + public UInt32 crc32; + [JsonProperty("offset")] + public UInt32 offset; + } +} diff --git a/Qiniu/IO/Resumable/Settings.cs b/Qiniu/IO/Resumable/Settings.cs new file mode 100644 index 00000000..8bffe436 --- /dev/null +++ b/Qiniu/IO/Resumable/Settings.cs @@ -0,0 +1,40 @@ + +namespace Qiniu.IO.Resumable +{ + /// + /// 断点续传上传参数设置 + /// + public class Settings + { + int chunkSize; + + /// + /// chunk大小,默认为256kb + /// + public int ChunkSize { + get { return chunkSize; } + set { chunkSize = value; } + } + + int tryTimes; + + /// + /// 失败重试次数,默认为3 + /// + public int TryTimes { + get { return tryTimes; } + set { tryTimes = value; } + } + + /// + /// 构造函数 + /// + /// chunk大小,默认为256kb + /// 失败重试次数,默认为3 + public Settings (int chunkSize=1 << 18, int tryTimes=3) + { + this.chunkSize = chunkSize; + this.tryTimes = tryTimes; + } + } +} diff --git a/Qiniu/Main.cs b/Qiniu/Main.cs new file mode 100644 index 00000000..59f40ec4 --- /dev/null +++ b/Qiniu/Main.cs @@ -0,0 +1,56 @@ +using System; +using Qiniu.IO; +using Qiniu.RS; +using Qiniu.Util; +using Qiniu.Conf; + +namespace csharptest +{ + class MainClass + { + + static string Bucket = "icattlecoder3"; + static string LocalKey = "gogopher.jpg"; + static string DOMAIN = "qiniuphotos.qiniudn.com"; + static string LocalFile = @"~/.profile"; + static string BigFile = @"C:\Users\floyd\Downloads\ChromeSetup.exe"; + static string FileOpUrl = "http://qiniuphotos.qiniudn.com/gogopher.jpg"; + static string NewKey + { + get { return Guid.NewGuid().ToString(); } + } + + public static void Main (string[] args) + { + Config.ACCESS_KEY = "gPhMyVzzbQ_LOjboaVsy7dbCB4JHgyVPonmhT3Dp"; + Config.SECRET_KEY = "OjY7IMysXu1erRRuWe7gkaiHcD6-JMJ4hXeRPZ1B"; + + PutTest (); + + Console.WriteLine ("Hello World!"); + Console.Write (""); + } + + public static void PutTest() + { + IOClient target = new IOClient(); + string upToken = string.Empty; + string key = LocalKey; + //PrintLn(key); + PutExtra extra = new PutExtra(); // TODO: 初始化为适当的值 + extra.MimeType = "text/plain"; + extra.Crc32 = 123; + extra.CheckCrc = CheckCrcType.CHECK; + extra.Params = new System.Collections.Generic.Dictionary(); + extra.Scope = Bucket+":"+key; + PutPolicy put = new PutPolicy(extra.Scope); + + PutRet ret = target.Put(put.Token(), key, "hello Qiniu Cloud!".ToStream(), extra); + + //Assert.IsTrue(ret.OK, "PutFileTest Failure"); + + } + } +} + + diff --git a/QBox/Properties/AssemblyInfo.cs b/Qiniu/Properties/AssemblyInfo.cs similarity index 88% rename from QBox/Properties/AssemblyInfo.cs rename to Qiniu/Properties/AssemblyInfo.cs index cc843a76..25f4ea8c 100644 --- a/QBox/Properties/AssemblyInfo.cs +++ b/Qiniu/Properties/AssemblyInfo.cs @@ -1,33 +1,33 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("QBox")] -[assembly: AssemblyDescription("csharp-sdk")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Qiniu Information Technologies")] -[assembly: AssemblyProduct("")] -[assembly: AssemblyCopyright("Copyright © 2012")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("f036fae6-2ac0-4b5a-8e66-60220f1b8c6c")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Qiniu")] +[assembly: AssemblyDescription("csharp-sdk")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Qiniu Information Technologies")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Copyright © 2012")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("f036fae6-2ac0-4b5a-8e66-60220f1b8c6c")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +[assembly: AssemblyVersion("6.0.0.0")] +[assembly: AssemblyFileVersion("6.0.0.0")] diff --git a/QBox/QBox.csproj b/Qiniu/Qiniu.csproj similarity index 51% rename from QBox/QBox.csproj rename to Qiniu/Qiniu.csproj index 8f48cd55..eadfbddc 100644 --- a/QBox/QBox.csproj +++ b/Qiniu/Qiniu.csproj @@ -5,14 +5,11 @@ x86 8.0.30703 2.0 - {1C8C9909-57ED-44E4-81A5-0904E96FA00E} + {AD4EA9D1-11C2-4BF6-8A06-72A966BC1B80} Library Properties - QBox - QBox - v4.0 - - + Qiniu + Qiniu 512 @@ -24,6 +21,7 @@ DEBUG;TRACE prompt 4 + bin\Debug\Qiniu.XML x86 @@ -34,59 +32,70 @@ prompt 4 - - - - - - + + + Code + + + + - - - - - - - - - - - - + + + + + + + + + + + + + - + + + + - - - - - - - - + + + - - - - - - + + + + + + + + + + ..\tools\Newtonsoft.Json.dll + + + - \ No newline at end of file + + + + + diff --git a/Qiniu/RPC/CallRet.cs b/Qiniu/RPC/CallRet.cs new file mode 100644 index 00000000..df692594 --- /dev/null +++ b/Qiniu/RPC/CallRet.cs @@ -0,0 +1,35 @@ +using System; +using System.Net; + +namespace Qiniu.RPC +{ + public class CallRet : EventArgs + { + public HttpStatusCode StatusCode { get; protected set; } + + public Exception Exception { get; protected set; } + + public string Response { get; protected set; } + + public bool OK { get { return (int)StatusCode / 100 == 2; } } + + public CallRet (HttpStatusCode statusCode, string response) + { + StatusCode = statusCode; + Response = response; + } + + public CallRet (HttpStatusCode statusCode, Exception e) + { + StatusCode = statusCode; + Exception = e; + } + + public CallRet (CallRet ret) + { + StatusCode = ret.StatusCode; + Exception = ret.Exception; + Response = ret.Response; + } + } +} diff --git a/Qiniu/RPC/Client.cs b/Qiniu/RPC/Client.cs new file mode 100644 index 00000000..e882b86d --- /dev/null +++ b/Qiniu/RPC/Client.cs @@ -0,0 +1,61 @@ +using System; +using System.IO; +using System.Net; + +namespace Qiniu.RPC +{ + public class Client + { + public virtual void SetAuth (HttpWebRequest request, Stream body) + { + } + + public CallRet Call (string url) + { + Console.WriteLine ("Client.Post ==> URL: " + url); + try { + HttpWebRequest request = (HttpWebRequest)WebRequest.Create (url); + request.UserAgent = Conf.Config.USER_AGENT; + request.Method = "POST"; + SetAuth (request, null); + using (HttpWebResponse response = request.GetResponse() as HttpWebResponse) { + return HandleResult (response); + } + } catch (Exception e) { + Console.WriteLine (e.ToString ()); + return new CallRet (HttpStatusCode.BadRequest, e); + } + } + + public CallRet CallWithBinary (string url, string contentType, Stream body, long length) + { + Console.WriteLine ("Client.PostWithBinary ==> URL: {0} Length:{1}", url, length); + try { + HttpWebRequest request = (HttpWebRequest)WebRequest.Create (url); + request.UserAgent = Conf.Config.USER_AGENT; + request.Method = "POST"; + request.ContentType = contentType; + request.ContentLength = length; + SetAuth (request, body); + using (Stream requestStream = request.GetRequestStream()) { + Util.IO.CopyN (requestStream, body, length); + } + using (HttpWebResponse response = request.GetResponse() as HttpWebResponse) { + return HandleResult (response); + } + } catch (Exception e) { + Console.WriteLine (e.ToString ()); + return new CallRet (HttpStatusCode.BadRequest, e); + } + } + + public static CallRet HandleResult (HttpWebResponse response) + { + HttpStatusCode statusCode = response.StatusCode; + using (StreamReader reader = new StreamReader(response.GetResponseStream())) { + string responseStr = reader.ReadToEnd (); + return new CallRet (statusCode, responseStr); + } + } + } +} \ No newline at end of file diff --git a/Qiniu/RS/BatchRet.cs b/Qiniu/RS/BatchRet.cs new file mode 100644 index 00000000..7ada571f --- /dev/null +++ b/Qiniu/RS/BatchRet.cs @@ -0,0 +1,117 @@ +using System; +using Newtonsoft.Json; + +namespace Qiniu.RS +{ + public class BatchRetItem + { + public int code; + public BatchRetData data; + } + + [JsonObject(MemberSerialization.OptIn)] + public class BatchRetData + { + Int64 fSize; + + /// + /// 文件大小. + /// + [JsonProperty("fsize")] + public Int64 FSize { + get { + return fSize; + } + set { + fSize = value; + } + } + + Int64 putTime; + + /// + /// 修改时间. + /// + [JsonProperty("putTime")] + public Int64 PutTime { + get { + return putTime; + } + set { + putTime = value; + } + } + + string key; + + /// + /// 文件名. + /// + [JsonProperty("key")] + public string Key { + get { + return key; + } + set { + key = value; + } + } + + string hash; + + /// + /// Gets a value indicating whether this instance hash. + /// + [JsonProperty("hash")] + public string Hash { + get { + return hash; + } + set { + hash = value; + } + } + + string mime; + + /// + /// Gets the MIME. + /// + [JsonProperty("mimeType")] + public string Mime { + get { + return mime; + } + set { + mime = value; + } + } + + string endUser; + + public string EndUser { + get { + return endUser; + } + set { + endUser = value; + } + } + + string error; + + /// + /// + /// + [JsonProperty("error")] + public string Error { + get { + return error; + } + set { + error = value; + } + + } + } +} diff --git a/Qiniu/RS/Entry.cs b/Qiniu/RS/Entry.cs new file mode 100644 index 00000000..839b1169 --- /dev/null +++ b/Qiniu/RS/Entry.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using Qiniu.RPC; + +namespace Qiniu.RS +{ + public class Entry : CallRet + { + /// + /// 文件的Hash值 + /// + /// true if this instance hash; otherwise, false. + public string Hash { get; private set; } + + /// + /// 文件的大小(单位: 字节) + /// + /// The fsize. + public long Fsize { get; private set; } + + /// + /// 文件上传到七牛云的时间(Unix时间戳) + /// + /// The put time. + public long PutTime { get; private set; } + + /// + /// 文件的媒体类型,比如"image/gif" + /// + /// The type of the MIME. + public string MimeType { get; private set; } + + /// + /// Gets the customer. + /// + /// The customer. + public string Customer { get; private set; } + + public Entry (CallRet ret) + : base(ret) + { + if (OK && string.IsNullOrEmpty (Response)) { + try { + Unmarshal (Response); + } catch (Exception e) { + Console.WriteLine (e.ToString ()); + this.Exception = e; + } + } + } + + private void Unmarshal (string json) + { + var dict = JsonConvert.DeserializeObject> (json); + if (dict != null) { + dynamic tmp; + if (dict.TryGetValue ("hash", out tmp)) { + Hash = (string)tmp; + } + if (dict.TryGetValue ("mimeType", out tmp)) { + MimeType = (string)tmp; + } + if (dict.TryGetValue ("fsize", out tmp)) { + Fsize = (long)tmp; + } + if (dict.TryGetValue ("putTime", out tmp)) { + PutTime = (long)tmp; + } + if (dict.TryGetValue ("customer", out tmp)) { + Customer = (string)tmp; + } + } + } + } +} diff --git a/Qiniu/RS/GetPolicy.cs b/Qiniu/RS/GetPolicy.cs new file mode 100644 index 00000000..1a56b428 --- /dev/null +++ b/Qiniu/RS/GetPolicy.cs @@ -0,0 +1,35 @@ +using System; +using System.Linq; +using Qiniu.Auth.digest; +using Qiniu.Conf; + +namespace Qiniu.RS +{ + /// + /// GetPolicy + /// + public class GetPolicy + { + public static string MakeRequest (string baseUrl, UInt32 expires = 3600, Mac mac = null) + { + if (mac == null) { + mac = new Mac (Config.ACCESS_KEY, Config.Encoding.GetBytes (Config.SECRET_KEY)); + } + + UInt32 deadline = (UInt32)((DateTime.Now.ToUniversalTime().Ticks - 621355968000000000) / 10000000 + expires); + if (baseUrl.Contains ('?')) { + baseUrl += "&e="; + } else { + baseUrl += "?e="; + } + baseUrl += deadline; + string token = mac.Sign (Conf.Config.Encoding.GetBytes (baseUrl)); + return string.Format ("{0}&token={1}", baseUrl, token); + } + + public static string MakeBaseUrl (string domain, string key) + { + return string.Format ("http://{0}/{1}", domain, System.Web.HttpUtility.UrlEncode (key)); + } + } +} diff --git a/Qiniu/RS/PutPolicy.cs b/Qiniu/RS/PutPolicy.cs new file mode 100644 index 00000000..5d5f17b3 --- /dev/null +++ b/Qiniu/RS/PutPolicy.cs @@ -0,0 +1,119 @@ +using System; +using Newtonsoft.Json; +using Qiniu.Auth.digest; +using Qiniu.Conf; +using Qiniu.Util; + +namespace Qiniu.RS +{ + /// + /// PutPolicy + /// + [JsonObject(MemberSerialization.OptIn)] + public class PutPolicy + { + private string scope; + private string callBackUrl; + private string callBackBody; + private string returnUrl; + private string returnBody; + private string asyncOps; + private string endUser; + private UInt64 expires = 3600; + + /// + /// 一般指文件要上传到的目标存储空间(Bucket)。若为”Bucket”,表示限定只能传到该Bucket(仅限于新增文件);若为”Bucket:Key”,表示限定特定的文件,可修改该文件。 + /// + [JsonProperty("scope")] + public string Scope { + get { return scope; } + set { scope = value; } + } + + /// + /// 文件上传成功后,Qiniu-Cloud-Server 向 App-Server 发送POST请求的URL,必须是公网上可以正常进行POST请求并能响应 HTTP Status 200 OK 的有效 URL + /// + [JsonProperty("callBackUrl")] + public string CallBackUrl { + get { return callBackUrl; } + set { callBackUrl = value; } + } + + /// + /// 文件上传成功后,Qiniu-Cloud-Server 向 App-Server 发送POST请求的数据。支持 魔法变量 和 自定义变量,不可与 returnBody 同时使用。 + /// + [JsonProperty("callBackBody")] + public string CallBackBody { + get { return callBackBody; } + set { callBackBody = value; } + } + + /// + /// 设置用于浏览器端文件上传成功后,浏览器执行301跳转的URL,一般为 HTML Form 上传时使用。文件上传成功后会跳转到 returnUrl?query_string, query_string 会包含 returnBody 内容。returnUrl 不可与 callbackUrl 同时使用 + /// + [JsonProperty("returnUrl")] + public string ReturnUrl { + get { return returnUrl; } + set { returnUrl = value; } + } + + /// + /// 文件上传成功后,自定义从 Qiniu-Cloud-Server 最终返回給终端 App-Client 的数据。支持 魔法变量,不可与 callbackBody 同时使用。 + /// + [JsonProperty("returnBody")] + public string ReturnBody { + get { return returnBody; } + set { returnBody = value; } + } + + /// + /// 指定文件(图片/音频/视频)上传成功后异步地执行指定的预转操作。每个预转指令是一个API规格字符串,多个预转指令可以使用分号“;”隔开 + /// + [JsonProperty("asyncOps")] + public string AsyncOps { + get { return asyncOps; } + set { asyncOps = value; } + } + + /// + /// 给上传的文件添加唯一属主标识,特殊场景下非常有用,比如根据终端用户标识给图片或视频打水印 + /// + [JsonProperty("endUser")] + public string EndUser { + get { return endUser; } + set { endUser = value; } + } + + /// + /// 定义 uploadToken 的失效时间,Unix时间戳,精确到秒,缺省为 3600 秒 + /// + [JsonProperty("deadline")] + public UInt64 Expires { + get { return expires; } + set { expires = value; } + } + + public PutPolicy (string scope, UInt32 expires=3600) + { + Scope = scope; + DateTime begin = new DateTime (1970, 1, 1); + DateTime now = DateTime.Now; + TimeSpan interval = new TimeSpan (now.Ticks - begin.Ticks); + Expires = (UInt32)interval.TotalSeconds + expires; + } + + /// + /// 生成上传Token + /// + /// + public string Token (Mac mac=null) + { + if (mac == null) { + mac = new Mac (Config.ACCESS_KEY, Config.Encoding.GetBytes (Config.SECRET_KEY)); + } + string flag = QiniuJsonHelper.JsonEncode (this); + return mac.SignWithData (Config.Encoding.GetBytes (flag)); + + } + } +} diff --git a/Qiniu/RS/RSClient.cs b/Qiniu/RS/RSClient.cs new file mode 100644 index 00000000..ac5c9a98 --- /dev/null +++ b/Qiniu/RS/RSClient.cs @@ -0,0 +1,241 @@ +using System.Collections.Generic; +using System.Text; +using Newtonsoft.Json; +using Qiniu.Auth; +using Qiniu.Auth.digest; +using Qiniu.Conf; +using Qiniu.RPC; +using Qiniu.Util; + +namespace Qiniu.RS +{ + /// + /// 文件管理操作 + /// + public enum FileHandle + { + /// + /// 查看 + /// + STAT = 0, + /// + /// 移动move + /// + MOVE, + /// + /// 复制copy + /// + COPY, + /// + /// 删除delete + /// + DELETE + } + + /// + /// 资源存储客户端,提供对文件的查看(stat),移动(move),复制(copy),删除(delete)操作 + /// 以及与这些操作对应的批量操作 + /// + public class RSClient :QiniuAuthClient + { + private static string[] OPS = new string[] { "stat", "move", "copy", "delet" }; + + public RSClient (Mac mac=null) + : base(mac) + { + } + + /// + /// + /// + /// + /// + /// + private CallRet op (FileHandle op, EntryPath scope) + { + string url = string.Format ("{0}/{1}/{2}", + Config.RS_HOST, + OPS [(int)op], + Base64URLSafe.Encode (scope.URI)); + return Call (url); + } + + /// + /// + /// + /// + /// + /// + private CallRet op2 (FileHandle op, EntryPathPair pair) + { + string url = string.Format ("{0}/{1}/{2}/{3}", + Config.RS_HOST, + OPS [(int)op], + Base64URLSafe.Encode (pair.URISrc), + Base64URLSafe.Encode (pair.URIDest)); + return Call (url); + } + + /// + /// 文件信息查看 + /// + /// + /// 文件的基本信息,见Entry + public Entry Stat (EntryPath scope) + { + CallRet callRet = op (FileHandle.STAT, scope); + return new Entry (callRet); + } + + /// + /// 删除文件 + /// + /// 七牛云存储空间名称 + /// 需要删除的文件key + /// + public CallRet Delete (EntryPath scope) + { + CallRet callRet = op (FileHandle.DELETE, scope); + return new Entry (callRet); + } + + /// + /// 移动文件 + /// + /// 文件所属的源空间名称 + /// 源key + /// 目标空间名称 + /// 目标key + /// CallRet + public CallRet Move (EntryPathPair pathPair) + { + return op2 (FileHandle.MOVE, pathPair); + } + + /// + /// 复制 + /// + /// 文件所属的空间名称 + /// 需要复制的文件key + /// 复制至目标空间 + /// 复制的副本文件key + /// CallRet + public CallRet Copy (EntryPathPair pathPair) + { + return op2 (FileHandle.COPY, pathPair); + } + + /// + /// 获取一元批操作http request Body + /// + /// 操作名 + /// 操作对象keys + /// Request Body + private string getBatchOp_1 (FileHandle op, EntryPath[] keys) + { + if (keys.Length < 1) + return string.Empty; + StringBuilder sb = new StringBuilder (); + for (int i = 0; i < keys.Length - 1; i++) { + string item = string.Format ("op=/{0}/{1}&", + OPS [(int)op], + Base64URLSafe.Encode (keys [i].URI)); + sb.Append (item); + } + string litem = string.Format ("op=/{0}/{1}", OPS [(int)op], Base64URLSafe.Encode (keys [keys.Length - 1].URI)); + return sb.Append (litem).ToString (); + } + + /// + /// + /// + /// + /// + /// + private string getBatchOp_2 (FileHandle op, EntryPathPair[] keys) + { + if (keys.Length < 1) + return string.Empty; + StringBuilder sb = new StringBuilder (); + for (int i = 0; i < keys.Length - 1; i++) { + string item = string.Format ("op=/{0}/{1}/{2}&", + OPS [(int)op], + Base64URLSafe.Encode (keys [i].URISrc), + Base64URLSafe.Encode (keys [i].URIDest)); + sb.Append (item); + } + string litem = string.Format ("op=/{0}/{1}/{2}", OPS [(int)op], + Base64URLSafe.Encode (keys [keys.Length - 1].URISrc), + Base64URLSafe.Encode (keys [keys.Length - 1].URIDest)); + return sb.Append (litem).ToString (); + } + + private CallRet batch (string requestBody) + { + return CallWithBinary (Conf.Config.RS_HOST + "/batch", "application/x-www-form-urlencoded", requestBody.ToStream (), requestBody.Length); + } + + /// + /// 批操作:文件信息查看 + /// + /// + /// public static void BatchStat(string bucket, string[] keys) + ///{ + /// RSClient client = new RSClient(); + /// List scopes= new List(); + /// foreach(string key in keys) + /// { + /// Console.WriteLine("\n===> Stat {0}:{1}", bucket, key); + /// scopes.Add(new Scope(bucket,key)); + /// } + /// client.BatchStat(scopes.ToArray()); + ///} + /// + /// + /// + /// 文件bucket+key,see + /// + public List BatchStat (EntryPath[] keys) + { + string requestBody = getBatchOp_1 (FileHandle.STAT, keys); + CallRet ret = batch (requestBody); + if (ret.OK) { + List items = JsonConvert.DeserializeObject> (ret.Response); + return items; + } + return null; + } + + /// + /// 批操作:文件移动 + /// + /// EntryPathPair + public CallRet BatchMove (EntryPathPair[] entryPathPairs) + { + string requestBody = getBatchOp_2 (FileHandle.MOVE, entryPathPairs); + return batch (requestBody); + } + + /// + /// + /// + /// + /// + public CallRet BatchCopy (EntryPathPair[] entryPathPari) + { + string requestBody = getBatchOp_2 (FileHandle.COPY, entryPathPari); + return batch (requestBody); + } + + /// + /// 批量删除 + /// + /// + /// + public CallRet BatchDelete (EntryPath[] keys) + { + string requestBody = getBatchOp_1 (FileHandle.DELETE, keys); + return batch (requestBody); + } + } +} diff --git a/Qiniu/RS/RSPath.cs b/Qiniu/RS/RSPath.cs new file mode 100644 index 00000000..9382e895 --- /dev/null +++ b/Qiniu/RS/RSPath.cs @@ -0,0 +1,99 @@ +namespace Qiniu.RS +{ + /// + /// bucket+ ":"+ key + /// + public class EntryPath + { + private string bucket; + + /// + /// 七年云存储空间名 + /// + public string Bucket { + get { return bucket; } + } + + private string key; + + /// + /// 文件key + /// + public string Key { + get { return System.Web.HttpUtility.UrlEncode (key); } + } + + private string uri; + + /// + /// bucket+ ":"+ key + /// + public string URI { get { return this.uri; } } + + public string Base64EncodedURI { get { return Qiniu.Util.Base64URLSafe.Encode (this.uri); } } + + public EntryPath (string bucket, string key) + { + this.bucket = bucket; + this.key = key; + this.uri = this.bucket + ":" + Key; + } + } + + /// + /// 二元操作路径 + /// + public class EntryPathPair + { + private EntryPath src; + private EntryPath dest; + + private void _entryPathPair (string bucketSrc, string keySrc, string bucketDest, string keyDest) + { + this.src = new EntryPath (bucketSrc, keySrc); + this.dest = new EntryPath (bucketDest, keyDest); + } + + /// + /// Initializes a new instance of the class. + /// + public EntryPathPair (EntryPath src, EntryPath des) + { + this.src = src; + this.dest = des; + } + + /// + /// 二元操作路径构造函数 + /// + /// 源空间名称 + /// 源文件key + /// 目标空间名称 + /// 目文件key + public EntryPathPair (string bucketSrc, string keySrc, string bucketDest, string keyDest) + { + _entryPathPair (bucketSrc, keySrc, bucketDest, keyDest); + } + + /// + /// 二元操作路径构造函数 + /// + /// 源空间名称,目标空间名称 + /// 源文件key + /// 目文件key + public EntryPathPair (string bucket, string keySrc, string keyDest) + { + _entryPathPair (bucket, keySrc, bucket, keyDest); + } + + /// + /// bucketSrc+":"+keySrc + /// + public string URISrc { get { return src.URI; } } + + /// + /// bucketDest+":"+keyDest + /// + public string URIDest { get { return dest.URI; } } + } +} diff --git a/Qiniu/RSF/DumpItem.cs b/Qiniu/RSF/DumpItem.cs new file mode 100644 index 00000000..9584325e --- /dev/null +++ b/Qiniu/RSF/DumpItem.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; + +namespace Qiniu.RSF +{ + /// + /// Dump item文件信息. + /// + [JsonObject(MemberSerialization.OptIn)] + public class DumpItem + { + Int64 fSize; + + /// + /// 文件大小. + /// + [JsonProperty("fsize")] + public Int64 FSize { + get { + return fSize; + } + set { + fSize = value; + } + } + + Int64 putTime; + + /// + /// 修改时间. + /// + [JsonProperty("putTime")] + public Int64 PutTime { + get { + return putTime; + } + set { + putTime = value; + } + } + + string key; + + /// + /// 文件名. + /// + [JsonProperty("key")] + public string Key { + get { + return key; + } + set { + key = value; + } + } + + string hash; + + /// + /// Gets a value indicating whether this instance hash. + /// + [JsonProperty("hash")] + public string Hash { + get { + return hash; + } + set { + hash = value; + } + } + + string mime; + + /// + /// Gets the MIME. + /// + [JsonProperty("mimeType")] + public string Mime { + get { + return mime; + } + set { + mime = value; + } + } + + string endUser; + + public string EndUser { + get { + return endUser; + } + set { + endUser = value; + } + } + } + + /// + /// Fetch 返回结果. + /// + [JsonObject(MemberSerialization.OptIn)] + public class DumpRet + { + string marker; + + /// + /// fetch 定位符 + /// + [JsonProperty("marker")] + public string Marker { + get { + return marker; + } + set { + marker = value; + } + } + + List items; + + /// + /// The items. + /// + [JsonProperty("items")] + public List Items { + get { + return items; + } + set { + items = value; + } + } + } +} + diff --git a/Qiniu/RSF/RSFClient.cs b/Qiniu/RSF/RSFClient.cs new file mode 100644 index 00000000..2217e84f --- /dev/null +++ b/Qiniu/RSF/RSFClient.cs @@ -0,0 +1,170 @@ +using System; +using System.Collections.Generic; +using Newtonsoft.Json; +using Qiniu.Auth; +using Qiniu.Conf; +using Qiniu.RPC; + +namespace Qiniu.RSF +{ + /// + /// RS Fetch + /// + public class RSFClient : QiniuAuthClient + { + private const int MAX_LIMIT = 1000; + //失败重试次数 + private const int RETRY_TIME = 3; + private string bucketName; + + /// + /// bucket name + /// + public string BucketName { get; private set; } + + private int limit; + + /// + /// Fetch返回结果条目数量限制 + /// + public int Limit { + get { + return limit; + } + set { + limit = value > MAX_LIMIT ? MAX_LIMIT : value; + } + } + + private bool end = false; + private string prefix; + + /// + /// 文件前缀 + /// + /// + /// The prefix. + /// + public string Prefix { + get { + return prefix; + } + set { + prefix = value; + } + } + + private string marker; + + /// + /// Fetch 定位符. + /// + public string Marker { + get { + return marker; + } + set { + marker = value; + } + } + + /// + /// RS Fetch Client + /// + /// 七牛云存储空间名称 + public RSFClient (string bucketName) + { + this.bucketName = bucketName; + } + + /// + /// The origin Fetch interface,we recomment to use Next(). + /// + /// + /// Dump + /// + /// + /// Bucket name. + /// + /// + /// Prefix. + /// + /// + /// Marker in. + /// + /// + /// Limit. + /// + public DumpRet ListPrefix (string bucketName, string prefix="", string markerIn="", int limit = MAX_LIMIT) + { + string url = Config.RSF_HOST + string.Format ("/list?bucket={0}", bucketName);// + bucketName + + if (!string.IsNullOrEmpty (markerIn)) { + url += string.Format ("&marker={0}", markerIn); + } + if (!string.IsNullOrEmpty (prefix)) { + url += string.Format ("&prefix={0}", prefix); + } + if (limit > 0) { + limit = limit > MAX_LIMIT ? MAX_LIMIT : limit; + url += string.Format ("&limit={0}", limit); + } + for (int i = 0; i < RETRY_TIME; i++) { + CallRet ret = Call (url); + if (ret.OK) { + return JsonConvert.DeserializeObject (ret.Response); + } else { + throw new Exception (string.Format ("listPrefix fail ===> {0}", ret.Exception.Message)); + } + } + return null; + } + + /// + /// call this func before invoke Next() + /// + public void Init () + { + end = false; + this.marker = string.Empty; + } + + /// + /// Next. + /// + /// + /// public static void List (string bucket) + ///{ + /// RSF.RSFClient rsf = new Qiniu.RSF.RSFClient(bucket); + /// rsf.Prefix = "test"; + /// rsf.Limit = 100; + /// List items; + /// while ((items=rsf.Next())!=null) + /// { + /// //todo + /// } + ///}s + /// + /// + /// + public List Next () + { + if (end) { + return null; + } + try { + DumpRet ret = ListPrefix (this.bucketName, this.prefix, this.marker, this.limit); + if (ret.Items.Count == 0) { + end = true; + return null; + } + this.marker = ret.Marker; + if (this.marker == null) + end = true; + return ret.Items; + } catch (Exception e) { + throw e; + } + } + } +} + diff --git a/Qiniu/Util/Base64UrlSafe.cs b/Qiniu/Util/Base64UrlSafe.cs new file mode 100644 index 00000000..46459c10 --- /dev/null +++ b/Qiniu/Util/Base64UrlSafe.cs @@ -0,0 +1,37 @@ +using System; +using System.Text; + +namespace Qiniu.Util +{ + public static class Base64URLSafe + { + public static string Encode (string text) + { + if (String.IsNullOrEmpty (text)) + return ""; + byte[] bs = Encoding.UTF8.GetBytes (text); + string encodedStr = Convert.ToBase64String (bs); + encodedStr = encodedStr.Replace ('+', '-').Replace ('/', '_'); + return encodedStr; + } + + /// + /// string扩展方法,生成base64UrlSafe + /// + /// + /// + public static string ToBase64URLSafe (this string str) + { + return Encode (str); + } + + public static string Encode (byte[] bs) + { + if (bs == null || bs.Length == 0) + return ""; + string encodedStr = Convert.ToBase64String (bs); + encodedStr = encodedStr.Replace ('+', '-').Replace ('/', '_'); + return encodedStr; + } + } +} diff --git a/Qiniu/Util/CRC32.cs b/Qiniu/Util/CRC32.cs new file mode 100644 index 00000000..01ea3786 --- /dev/null +++ b/Qiniu/Util/CRC32.cs @@ -0,0 +1,76 @@ +using System; +using System.IO; + +namespace Qiniu.Util +{ + public class CRC32 + { + public const UInt32 IEEE = 0xedb88320; + private UInt32[] Table; + private UInt32 Value; + + public CRC32 () + { + Value = 0; + Table = MakeTable (IEEE); + } + + public void Write (byte[] p, int offset, int count) + { + this.Value = Update (this.Value, this.Table, p, offset, count); + } + + public UInt32 Sum32 () + { + return this.Value; + } + + private static UInt32[] MakeTable (UInt32 poly) + { + UInt32[] table = new UInt32[256]; + for (int i = 0; i < 256; i++) { + UInt32 crc = (UInt32)i; + for (int j = 0; j < 8; j++) { + if ((crc & 1) == 1) + crc = (crc >> 1) ^ poly; + else + crc >>= 1; + } + table [i] = crc; + } + return table; + } + + public static UInt32 Update (UInt32 crc, UInt32[] table, byte[] p, int offset, int count) + { + crc = ~crc; + for (int i = 0; i < count; i++) { + crc = table [((byte)crc) ^ p [offset + i]] ^ (crc >> 8); + } + return ~crc; + } + + public static UInt32 CheckSumBytes (byte[] data) + { + CRC32 crc = new CRC32 (); + crc.Write (data, 0, data.Length); + return crc.Sum32 (); + } + + public static UInt32 CheckSumFile (string fileName) + { + CRC32 crc = new CRC32 (); + int bufferLen = 32 * 1024; + using (FileStream fs = File.OpenRead(fileName)) { + byte[] buffer = new byte[bufferLen]; + while (true) { + int n = fs.Read (buffer, 0, bufferLen); + if (n == 0) + break; + crc.Write (buffer, 0, n); + } + } + return crc.Sum32 (); + } + } +} diff --git a/QBox/Util/StreamUtil.cs b/Qiniu/Util/IO.cs similarity index 74% rename from QBox/Util/StreamUtil.cs rename to Qiniu/Util/IO.cs index 1a94a85d..4cf8df8e 100644 --- a/QBox/Util/StreamUtil.cs +++ b/Qiniu/Util/IO.cs @@ -1,46 +1,48 @@ -using System; -using System.Text; -using System.IO; - -namespace QBox.Util -{ - public static class StreamUtil - { - public static int bufferLen = 32*1024; - - public static void Copy(Stream src, Stream dst) - { - byte[] buffer = new byte[bufferLen]; - while (true) - { - int n = src.Read(buffer, 0, bufferLen); - if (n == 0) break; - dst.Write(buffer, 0, n); - } - } - - public static void CopyN(Stream src, Stream dst, long numBytesToCopy) - { - Console.WriteLine("Stream.CopyN: {0}", numBytesToCopy); - byte[] buffer = new byte[bufferLen]; - long numBytesWritten = 0; - while (numBytesWritten < numBytesToCopy) - { - int len = bufferLen; - if ((numBytesToCopy - numBytesWritten) < len) - { - len = (int)(numBytesToCopy - numBytesWritten); - } - int n = src.Read(buffer, 0, len); - if (n == 0) break; - dst.Write(buffer, 0, n); - numBytesWritten += n; - //Console.WriteLine("Stream.CopyN.Write: {0} {1}", n, numBytesWritten); - } - if (numBytesWritten != numBytesToCopy) - { - throw new Exception("StreamUtil.CopyN: nwritten not equal to ncopy"); - } - } - } -} +using System; +using System.Text; +using System.IO; + +namespace Qiniu.Util +{ + public static class IO + { + public static int bufferLen = 32*1024; + + public static void Copy(Stream dst, Stream src) + { + long l =src.Position; + byte[] buffer = new byte[bufferLen]; + while (true) + { + int n = src.Read(buffer, 0, bufferLen); + if (n == 0) break; + dst.Write(buffer, 0, n); + } + src.Seek (l, SeekOrigin.Begin); + } + + public static void CopyN(Stream dst, Stream src, long numBytesToCopy) + { + long l =src.Position; + byte[] buffer = new byte[bufferLen]; + long numBytesWritten = 0; + while (numBytesWritten < numBytesToCopy) + { + int len = bufferLen; + if ((numBytesToCopy - numBytesWritten) < len) + { + len = (int)(numBytesToCopy - numBytesWritten); + } + int n = src.Read(buffer, 0, len); + if (n == 0) break; + dst.Write(buffer, 0, n); + numBytesWritten += n; + } + src.Seek (l, SeekOrigin.Begin); + if (numBytesWritten != numBytesToCopy) + { + throw new Exception("StreamUtil.CopyN: nwritten not equal to ncopy"); + } + } + } +} diff --git a/Qiniu/Util/QiniuJsonHelper.cs b/Qiniu/Util/QiniuJsonHelper.cs new file mode 100644 index 00000000..72aa66c3 --- /dev/null +++ b/Qiniu/Util/QiniuJsonHelper.cs @@ -0,0 +1,19 @@ +using Newtonsoft.Json; + +namespace Qiniu.Util +{ + public static class QiniuJsonHelper + { + public static string JsonEncode (object obj) + { + JsonSerializerSettings setting = new JsonSerializerSettings (); + setting.NullValueHandling = NullValueHandling.Ignore; + return JsonConvert.SerializeObject (obj, setting); + } + + public static T ToObject (this string value) + { + return JsonConvert.DeserializeObject (value); + } + } +} diff --git a/Qiniu/Util/StreamEx.cs b/Qiniu/Util/StreamEx.cs new file mode 100644 index 00000000..c2570786 --- /dev/null +++ b/Qiniu/Util/StreamEx.cs @@ -0,0 +1,19 @@ +using System; +using System.IO; + +namespace Qiniu.Util +{ + public static class StreamEx + { + /// + /// string To Stream + /// + /// + /// + public static Stream ToStream (this string str) + { + Stream s = new MemoryStream (Conf.Config.Encoding.GetBytes (str)); + return s; + } + } +} diff --git a/Qiniu/Util/StringEx.cs b/Qiniu/Util/StringEx.cs new file mode 100644 index 00000000..927b378a --- /dev/null +++ b/Qiniu/Util/StringEx.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Qiniu.Util +{ + public static class StringEx + { + public static string ToUrlEncode (this string value) + { + return System.Web.HttpUtility.UrlEncode (value); + } + } +} diff --git a/Qiniu/app.config b/Qiniu/app.config new file mode 100644 index 00000000..336c0f40 --- /dev/null +++ b/Qiniu/app.config @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/README.md b/README.md index c2ce1131..90ecbd44 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ## 使用 -参考文档:[七牛云存储 C# SDK 使用指南](http://docs.qiniutek.com/v2/sdk/csharp/) +参考文档:[七牛云存储 C# SDK 使用指南](http://docs.qiniu.com/v2/sdk/csharp/) ## 贡献代码 diff --git a/bin/Qiniu.dll b/bin/Qiniu.dll new file mode 100755 index 00000000..b5b7b6e1 Binary files /dev/null and b/bin/Qiniu.dll differ diff --git a/csharp-sdk.sln b/csharp-sdk.sln index 2ad0e386..a81a6fad 100644 --- a/csharp-sdk.sln +++ b/csharp-sdk.sln @@ -1,24 +1,54 @@  Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual C# Express 2010 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "QBox", "QBox\QBox.csproj", "{1C8C9909-57ED-44E4-81A5-0904E96FA00E}" +# Visual Studio 2010 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SDK", "SDK", "{65603FA4-DDC3-4FD7-ADA3-7BCC2161A247}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Demo", "Demo\Demo.csproj", "{8BA368D7-3784-4C50-9A28-4FA1A9C81555}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qiniu", "Qiniu\Qiniu.csproj", "{AD4EA9D1-11C2-4BF6-8A06-72A966BC1B80}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qiniu.Test", "Qiniu.Test\Qiniu.Test.csproj", "{95DC2A77-2344-4315-9F6F-334CC928459C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|Mixed Platforms = Debug|Mixed Platforms Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|Mixed Platforms = Release|Mixed Platforms Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {1C8C9909-57ED-44E4-81A5-0904E96FA00E}.Debug|x86.ActiveCfg = Debug|x86 - {1C8C9909-57ED-44E4-81A5-0904E96FA00E}.Debug|x86.Build.0 = Debug|x86 - {1C8C9909-57ED-44E4-81A5-0904E96FA00E}.Release|x86.ActiveCfg = Release|x86 - {1C8C9909-57ED-44E4-81A5-0904E96FA00E}.Release|x86.Build.0 = Release|x86 - {8BA368D7-3784-4C50-9A28-4FA1A9C81555}.Debug|x86.ActiveCfg = Debug|x86 - {8BA368D7-3784-4C50-9A28-4FA1A9C81555}.Debug|x86.Build.0 = Debug|x86 - {8BA368D7-3784-4C50-9A28-4FA1A9C81555}.Release|x86.ActiveCfg = Release|x86 - {8BA368D7-3784-4C50-9A28-4FA1A9C81555}.Release|x86.Build.0 = Release|x86 + {95DC2A77-2344-4315-9F6F-334CC928459C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {95DC2A77-2344-4315-9F6F-334CC928459C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {95DC2A77-2344-4315-9F6F-334CC928459C}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {95DC2A77-2344-4315-9F6F-334CC928459C}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {95DC2A77-2344-4315-9F6F-334CC928459C}.Debug|x86.ActiveCfg = Debug|Any CPU + {95DC2A77-2344-4315-9F6F-334CC928459C}.Debug|x86.Build.0 = Debug|Any CPU + {95DC2A77-2344-4315-9F6F-334CC928459C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {95DC2A77-2344-4315-9F6F-334CC928459C}.Release|Any CPU.Build.0 = Release|Any CPU + {95DC2A77-2344-4315-9F6F-334CC928459C}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {95DC2A77-2344-4315-9F6F-334CC928459C}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {95DC2A77-2344-4315-9F6F-334CC928459C}.Release|x86.ActiveCfg = Release|Any CPU + {95DC2A77-2344-4315-9F6F-334CC928459C}.Release|x86.Build.0 = Release|Any CPU + {AD4EA9D1-11C2-4BF6-8A06-72A966BC1B80}.Debug|Any CPU.ActiveCfg = Debug|x86 + {AD4EA9D1-11C2-4BF6-8A06-72A966BC1B80}.Debug|Mixed Platforms.ActiveCfg = Debug|x86 + {AD4EA9D1-11C2-4BF6-8A06-72A966BC1B80}.Debug|Mixed Platforms.Build.0 = Debug|x86 + {AD4EA9D1-11C2-4BF6-8A06-72A966BC1B80}.Debug|x86.ActiveCfg = Debug|x86 + {AD4EA9D1-11C2-4BF6-8A06-72A966BC1B80}.Debug|x86.Build.0 = Debug|x86 + {AD4EA9D1-11C2-4BF6-8A06-72A966BC1B80}.Release|Any CPU.ActiveCfg = Release|x86 + {AD4EA9D1-11C2-4BF6-8A06-72A966BC1B80}.Release|Mixed Platforms.ActiveCfg = Release|x86 + {AD4EA9D1-11C2-4BF6-8A06-72A966BC1B80}.Release|Mixed Platforms.Build.0 = Release|x86 + {AD4EA9D1-11C2-4BF6-8A06-72A966BC1B80}.Release|x86.ActiveCfg = Release|x86 + {AD4EA9D1-11C2-4BF6-8A06-72A966BC1B80}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {AD4EA9D1-11C2-4BF6-8A06-72A966BC1B80} = {65603FA4-DDC3-4FD7-ADA3-7BCC2161A247} + {95DC2A77-2344-4315-9F6F-334CC928459C} = {65603FA4-DDC3-4FD7-ADA3-7BCC2161A247} + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = Qiniu\Qiniu.csproj + EndGlobalSection + GlobalSection(TestCaseManagementSettings) = postSolution + CategoryFile = csharp-sdk.vsmdi EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/tools/Newtonsoft.Json.dll b/tools/Newtonsoft.Json.dll new file mode 100755 index 00000000..92e36fca Binary files /dev/null and b/tools/Newtonsoft.Json.dll differ diff --git a/tools/nunit.framework.dll b/tools/nunit.framework.dll new file mode 100644 index 00000000..3e24ba1c Binary files /dev/null and b/tools/nunit.framework.dll differ diff --git a/tools/nunit.util.dll b/tools/nunit.util.dll new file mode 100644 index 00000000..88aee952 Binary files /dev/null and b/tools/nunit.util.dll differ