diff --git a/CHANGELOG.md b/CHANGELOG.md index df7c3fb6..53bd8c56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ ## CHANGE LOG +### v6.1.1 +2014-04-28 issue [#45](https://github.com/qiniu/csharp-sdk/pull/45) +- [#41] [#42] 简化断点续上传,删除bput逻辑, 修复bug:>2.5GB文件上传失败 +- [#38] [#40] pfop 支持 + ### v6.1.0 2014-02-18 issue [#37](https://github.com/qiniu/csharp-sdk/pull/37) diff --git a/Docs/README.md b/Docs/README.md index 407ad2bf..909c62e5 100644 --- a/Docs/README.md +++ b/Docs/README.md @@ -58,6 +58,8 @@ DLL引用方式: C# SDK引用了第三方的开源项目 Json.NET,因此,您需要在项目中引用它 项目地址:[http://json.codeplex.com](http://json.codeplex.com)。 + +## 初始化 ### 配置密钥 @@ -424,18 +426,8 @@ 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 }; + PutExtra extra = new PutExtra (); 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); } ``` @@ -455,27 +447,12 @@ public static void ResumablePutFile(string bucket, string key, string fname) 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以下两个事件监听当前上传进度以及成功情况: +ResumablePut采用分快上传,各快之间采用并行上传,可以通过注册ResumablePutExtra以下两个事件监听当前上传进度以及成功情况: ```c# public event EventHandler Notify; @@ -498,7 +475,7 @@ public event EventHandler NotifyErr; 其中是bucket所对应的域名。七牛云存储为每一个bucket提供一个默认域名。默认域名可以到[七牛云存储开发者平台](https://portal.qiniu.com/)中,空间设置的域名设置一节查询。用户也可以将自有的域名绑定到bucket上,用户可以通过自有域名访问七牛云存储。 -**注意: key必须采用utf8编码,如使用非utf8编码访问七牛云存储将反馈错误** +**注意: key必须采用utf8编码,如使用非utf8编码访问七牛云存储将返回错误** #### 私有资源下载 diff --git a/Qiniu.Test/IO/Resumable/ResumablePutTest.cs b/Qiniu.Test/IO/Resumable/ResumablePutTest.cs index 2de68ba1..7a472060 100644 --- a/Qiniu.Test/IO/Resumable/ResumablePutTest.cs +++ b/Qiniu.Test/IO/Resumable/ResumablePutTest.cs @@ -34,7 +34,6 @@ public void ResumablePutFileTest() ResumablePut target = new ResumablePut(putSetting, extra); // TODO: 初始化为适当的值 Console.WriteLine ("extra.Bucket:"+Bucket); string upToken = new PutPolicy(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 (); @@ -63,12 +62,5 @@ void extra_Notify(object sender, PutNotifyEvent e) 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/IO/Resumable/ResumablePut.cs b/Qiniu/IO/Resumable/ResumablePut.cs index 22893e0a..b6ed3777 100644 --- a/Qiniu/IO/Resumable/ResumablePut.cs +++ b/Qiniu/IO/Resumable/ResumablePut.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Text; #if NET40 @@ -21,14 +22,6 @@ public class ResumablePut private const int blockMashk = (1 << blockBits) - 1; private static int BLOCKSIZE = 4 * 1024 * 1024; - #region 记录总文件大小,用于计算上传百分比 - - private long fsize; - private float chunks; - private float uploadedChunks = 0; - - #endregion - /// /// 上传完成事件 /// @@ -37,10 +30,6 @@ public class ResumablePut /// 上传Failure事件 /// public event EventHandler PutFailure; - /// - /// 进度提示事件 - /// - public event Action Progress; Settings putSetting; @@ -71,7 +60,6 @@ public ResumablePutExtra Extra /// public ResumablePut(Settings putSetting, ResumablePutExtra extra) { - extra.chunkSize = putSetting.ChunkSize; this.putSetting = putSetting; this.extra = extra; } @@ -88,36 +76,23 @@ public CallRet PutFile(string upToken, string localFile, string key) { throw new Exception(string.Format("{0} does not exist", localFile)); } + PutAuthClient client = new PutAuthClient(upToken); CallRet ret; using (FileStream fs = File.OpenRead(localFile)) { int block_cnt = block_count(fs.Length); - fsize = fs.Length; - chunks = fsize / extra.chunkSize + 1; + long fsize = fs.Length; extra.Progresses = new BlkputRet[block_cnt]; - //并行上传 -#if NET35||NET20 + byte[] byteBuf = new byte[BLOCKSIZE]; + int readLen = BLOCKSIZE; for (int i = 0; i < block_cnt; i++) { -#elif NET40 - Parallel.For(0, block_cnt, (i) =>{ -#endif - - int readLen = BLOCKSIZE; - if ((i + 1) * BLOCKSIZE > fsize) - readLen = (int)(fsize - i * BLOCKSIZE); - byte[] byteBuf = new byte[readLen]; -#if NET40 - lock (fs) - { -#endif - fs.Seek(i * BLOCKSIZE, SeekOrigin.Begin); + if (i == block_cnt - 1) { + readLen = (int)(fsize - (long)i * BLOCKSIZE); + } + fs.Seek((long)i * BLOCKSIZE, SeekOrigin.Begin); fs.Read(byteBuf, 0, readLen); -#if NET40 - } -#endif - //并行上传BLOCK BlkputRet blkRet = ResumableBlockPut(client, byteBuf, i, readLen); if (blkRet == null) { @@ -127,19 +102,11 @@ public CallRet PutFile(string upToken, string localFile, string key) { extra.OnNotify(new PutNotifyEvent(i, readLen, extra.Progresses[i])); } -#if NET35||NET20 } -#elif NET40 - }); -#endif - ret = Mkfile(client, key, fs.Length); + ret = Mkfile(client, key, fsize); } if (ret.OK) { - if (Progress != null) - { - Progress(1.0f); - } if (PutFinished != null) { PutFinished(this, ret); @@ -155,86 +122,49 @@ public CallRet PutFile(string upToken, string localFile, string key) return 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) + uint crc32 = CRC32.CheckSumBytes(body, blkSize); + for (int i = 0; i < putSetting.TryTimes; i++) { - 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++) + try { - 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; - } + extra.Progresses[blkIdex] = Mkblock(client, body, blkSize); } - } - #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++) + catch (Exception ee) { - extra.Progresses[blkIdex] = BlockPut(client, extra.Progresses[blkIdex], new MemoryStream(chunk), bodyLength); - if (extra.Progresses[blkIdex] == null) + if (i == (putSetting.TryTimes - 1)) { - if (i == (putSetting.TryTimes - 1)) - { - return null; - } - continue; + throw ee; } - else + System.Threading.Thread.Sleep(1000); + continue; + } + if (extra.Progresses[blkIdex] == null || crc32 != extra.Progresses[blkIdex].crc32) + { + if (i == (putSetting.TryTimes - 1)) { - uploadedChunks++; - if (Progress != null) - { - Progress((float)uploadedChunks / chunks); - } - break; + return null; } + System.Threading.Thread.Sleep(1000); + continue; + } + else + { + break; } } #endregion + return extra.Progresses[blkIdex]; - } + } - private BlkputRet Mkblock(Client client, byte[] firstChunk, long blkSize) + private BlkputRet Mkblock(Client client, byte[] firstChunk, int 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); + + CallRet callRet = client.CallWithBinary(url, "application/octet-stream",new MemoryStream(firstChunk, 0, blkSize),blkSize); if (callRet.OK) { return QiniuJsonHelper.ToObject(callRet.Response); @@ -242,17 +172,6 @@ private BlkputRet Mkblock(Client client, byte[] firstChunk, long blkSize) 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 QiniuJsonHelper.ToObject(callRet.Response); - } - return null; - } - private CallRet Mkfile(Client client, string key, long fsize) { StringBuilder urlBuilder = new StringBuilder(); diff --git a/Qiniu/IO/Resumable/ResumablePutRet.cs b/Qiniu/IO/Resumable/ResumablePutRet.cs index e7dfa6d0..a04828e2 100644 --- a/Qiniu/IO/Resumable/ResumablePutRet.cs +++ b/Qiniu/IO/Resumable/ResumablePutRet.cs @@ -13,6 +13,6 @@ public class BlkputRet [JsonProperty("crc32")] public UInt32 crc32; [JsonProperty("offset")] - public UInt32 offset; + public ulong offset; } } diff --git a/Qiniu/IO/Resumable/Settings.cs b/Qiniu/IO/Resumable/Settings.cs index 63331210..e439950b 100644 --- a/Qiniu/IO/Resumable/Settings.cs +++ b/Qiniu/IO/Resumable/Settings.cs @@ -6,14 +6,13 @@ namespace Qiniu.IO.Resumable /// public class Settings { - int chunkSize; - /// /// chunk大小,默认为4MB; + /// 兼容保留 /// public int ChunkSize { - get { return chunkSize; } - set { chunkSize = value; } + get; + set; } int tryTimes; @@ -32,8 +31,9 @@ public int TryTimes { /// chunk大小,默认为4MB /// 失败重试次数,默认为3 public Settings (int chunkSize=1 << 22, int tryTimes=3) - { - this.chunkSize = chunkSize; + { + //chunkSize 已经删除,兼容保留 + this.tryTimes = tryTimes; } } diff --git a/Qiniu/Qiniu.2.0.csproj b/Qiniu/Qiniu.2.0.csproj index 00539836..f38ad126 100644 --- a/Qiniu/Qiniu.2.0.csproj +++ b/Qiniu/Qiniu.2.0.csproj @@ -15,7 +15,6 @@ - x86 true full false @@ -101,4 +100,4 @@ - \ No newline at end of file + diff --git a/Qiniu/Qiniu.4.0.csproj b/Qiniu/Qiniu.4.0.csproj index 1734a9af..0535cc75 100644 --- a/Qiniu/Qiniu.4.0.csproj +++ b/Qiniu/Qiniu.4.0.csproj @@ -15,7 +15,7 @@ - x86 + AnyCPU true full false diff --git a/Qiniu/Util/CRC32.cs b/Qiniu/Util/CRC32.cs index 01ea3786..e8c638a9 100644 --- a/Qiniu/Util/CRC32.cs +++ b/Qiniu/Util/CRC32.cs @@ -50,10 +50,10 @@ public static UInt32 Update (UInt32 crc, UInt32[] table, byte[] p, int offset, i return ~crc; } - public static UInt32 CheckSumBytes (byte[] data) + public static UInt32 CheckSumBytes (byte[] data,int length) { CRC32 crc = new CRC32 (); - crc.Write (data, 0, data.Length); + crc.Write (data, 0, length); return crc.Sum32 (); } diff --git a/README.md b/README.md index 0c9be7a4..b50a66aa 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Qiniu Resource (Cloud) Storage SDK for C# +[![Build Status](https://api.travis-ci.org/qiniu/csharp-sdk.png?branch=master)](https://travis-ci.org/qiniu/csharp-sdk) + # 关于 此 C# SDK 适用于 .NET2.0 及以上版本,基于 [七牛云存储官方API](http://developer.qiniu.com/docs/v6/api/) 构建。使用此 SDK 构建您的网络应用程序,能让您以非常便捷地方式将数据安全地存储到七牛云存储上。无论您的网络应用是一个网站程序,还是包括从云端(服务端程序)到终端(手持设备应用)的架构的服务或应用,通过七牛云存储及其 SDK,都能让您应用程序的终端用户高速上传和下载,同时也让您的服务端更加轻盈。 @@ -18,7 +20,7 @@ ## 许可证 -Copyright (c) 2012 qiniutek.com +Copyright (c) 2014 qiniu.com 基于 MIT 协议发布: diff --git a/bin/Qiniu.2.0.dll b/bin/Qiniu.2.0.dll new file mode 100755 index 00000000..92cd6bae Binary files /dev/null and b/bin/Qiniu.2.0.dll differ diff --git a/bin/Qiniu.4.0.dll b/bin/Qiniu.4.0.dll index 3263456b..a0595c76 100755 Binary files a/bin/Qiniu.4.0.dll and b/bin/Qiniu.4.0.dll differ diff --git a/csharp-sdk.2.0.sln b/csharp-sdk.2.0.sln index f374e292..d3f062ea 100644 --- a/csharp-sdk.2.0.sln +++ b/csharp-sdk.2.0.sln @@ -3,9 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SDK", "SDK", "{65603FA4-DDC3-4FD7-ADA3-7BCC2161A247}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qiniu", "Qiniu\Qiniu.2.0.csproj", "{AD4EA9D1-11C2-4BF6-8A06-72A966BC1B80}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qiniu.2.0", "Qiniu\Qiniu.2.0.csproj", "{AD4EA9D1-11C2-4BF6-8A06-72A966BC1B80}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qiniu.Test", "Qiniu.Test\Qiniu.2.0.Test.csproj", "{95DC2A77-2344-4315-9F6F-334CC928459C}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qiniu.2.0.Test", "Qiniu.Test\Qiniu.2.0.Test.csproj", "{95DC2A77-2344-4315-9F6F-334CC928459C}" EndProject Global GlobalSection(TestCaseManagementSettings) = postSolution diff --git a/csharp-sdk.4.0.sln b/csharp-sdk.4.0.sln index a98455bc..7d25e03f 100644 --- a/csharp-sdk.4.0.sln +++ b/csharp-sdk.4.0.sln @@ -3,9 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SDK", "SDK", "{65603FA4-DDC3-4FD7-ADA3-7BCC2161A247}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qiniu", "Qiniu\Qiniu.4.0.csproj", "{AD4EA9D1-11C2-4BF6-8A06-72A966BC1B80}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qiniu.4.0", "Qiniu\Qiniu.4.0.csproj", "{AD4EA9D1-11C2-4BF6-8A06-72A966BC1B80}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qiniu.Test", "Qiniu.Test\Qiniu.4.0.Test.csproj", "{95DC2A77-2344-4315-9F6F-334CC928459C}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qiniu.4.0.Test", "Qiniu.Test\Qiniu.4.0.Test.csproj", "{95DC2A77-2344-4315-9F6F-334CC928459C}" EndProject Global GlobalSection(TestCaseManagementSettings) = postSolution