Skip to content

Commit b70f8f2

Browse files
authoredSep 20, 2024··
Bump to v8.6.0 (#264)
* feat: reorder default query regions hosts * feat: add `VerifyRequest` to check if request signed by qn * feat: more fop support - add idle-time fop support - add getting fop status - add configurable for pfop api host config * chore: bump version info to v8.6.0 and update changelog * feat: change default uc host
1 parent 6b250fe commit b70f8f2

14 files changed

+230
-20
lines changed
 

‎CHANGELOG.md

+10
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
**2024-09-05**
2+
3+
v8.6.0
4+
5+
新增:验证 Qbox, Qiniu 签名的辅助方法
6+
7+
新增:持久化处理,支持闲时任务
8+
9+
更改:对象存储,默认空间管理域名,查询区域主备域名
10+
111
**2024-08-23**
212

313
v8.5.1

‎src/Qiniu/Qiniu.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
<PostBuildEvent>
2929
</PostBuildEvent>
3030
<ProjectId>Qiniu</ProjectId>
31-
<Version>8.5.1</Version>
31+
<Version>8.6.0</Version>
3232
<Authors>Rong Zhou, Qiniu SDK</Authors>
3333
<Company>Shanghai Qiniu Information Technology Co., Ltd.</Company>
3434
<Description>Qiniu Resource (Cloud) Storage SDK for C#</Description>

‎src/Qiniu/QiniuCSharpSDK.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,6 @@ public class QiniuCSharpSDK
3737
/// <summary>
3838
/// SDK版本号
3939
/// </summary>
40-
public const string VERSION = "8.5.1";
40+
public const string VERSION = "8.6.0";
4141

4242
}

‎src/Qiniu/Storage/Config.cs

+4-4
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,18 @@ public class Config
1414
/// <summary>
1515
/// 默认空间管理域名
1616
/// </summary>
17-
public static string DefaultUcHost = "uc.qbox.me";
17+
public static string DefaultUcHost = "uc.qiniuapi.com";
1818
/// <summary>
1919
/// 默认查询区域域名
2020
/// </summary>
21-
public static string DefaultQueryRegionHost = "kodo-config.qiniuapi.com";
21+
public static string DefaultQueryRegionHost = "uc.qiniuapi.com";
2222
/// <summary>
2323
/// 默认备用查询区域域名
2424
/// </summary>
2525
public static List<string> DefaultBackupQueryRegionHosts = new List<string>
2626
{
27-
"uc.qbox.me",
28-
"api.qiniu.com"
27+
"kodo-config.qiniuapi.com",
28+
"uc.qbox.me"
2929
};
3030

3131
/// <summary>

‎src/Qiniu/Storage/OperationManager.cs

+14-1
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,17 @@ public OperationManager(Mac mac, Config config)
4141
/// <param name="pipeline">私有队列</param>
4242
/// <param name="notifyUrl">通知url</param>
4343
/// <param name="force">forece参数</param>
44+
/// <param name="persistentType">为 1 时开启闲时任务</param>
4445
/// <returns>pfop操作返回结果,正确返回结果包含persistentId</returns>
45-
public PfopResult Pfop(string bucket, string key, string fops, string pipeline, string notifyUrl, bool force)
46+
public PfopResult Pfop(
47+
string bucket,
48+
string key,
49+
string fops,
50+
string pipeline,
51+
string notifyUrl,
52+
bool force,
53+
int type = 0
54+
)
4655
{
4756
PfopResult result = new PfopResult();
4857

@@ -65,6 +74,10 @@ public PfopResult Pfop(string bucket, string key, string fops, string pipeline,
6574
{
6675
sb.AppendFormat("&pipeline={0}", pipeline);
6776
}
77+
if (type > 0)
78+
{
79+
sb.AppendFormat("&type={0}", type);
80+
}
6881
byte[] data = Encoding.UTF8.GetBytes(sb.ToString());
6982
string token = auth.CreateManageToken(pfopUrl, data);
7083

‎src/Qiniu/Storage/PfopInfo.cs

+10
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ public class PfopInfo
1313
[JsonProperty("id")]
1414
public string Id;
1515
/// <summary>
16+
/// 任务类型,为 1 代表为闲时任务
17+
/// </summary>
18+
[JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
19+
public int? Type;
20+
/// <summary>
21+
/// 任务创建时间
22+
/// </summary>
23+
[JsonProperty("creationDate", NullValueHandling = NullValueHandling.Ignore)]
24+
public string CreationDate;
25+
/// <summary>
1626
/// 任务结果状态码
1727
/// </summary>
1828
[JsonProperty("code")]

‎src/Qiniu/Storage/PrefopResult.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public PfopInfo Result
1919

2020
if ((Code == (int)HttpCode.OK) && (!string.IsNullOrEmpty(Text)))
2121
{
22-
info= JsonConvert.DeserializeObject<PfopInfo>(Text);
22+
info= JsonConvert.DeserializeObject<PfopInfo>(Text);
2323
}
2424
return info;
2525
}

‎src/Qiniu/Storage/PutPolicy.cs

+7
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,13 @@ public class PutPolicy
111111
[JsonProperty("persistentPipeline", NullValueHandling = NullValueHandling.Ignore)]
112112
public string PersistentPipeline { get; set; }
113113

114+
/// <summary>
115+
/// [可选]持久化任务类型,为 1 时开启闲时任务
116+
/// </summary>
117+
[JsonProperty("persistentType", NullValueHandling = NullValueHandling.Ignore)]
118+
public int? PersistentType { get; set; }
119+
120+
114121
/// <summary>
115122
/// [可选]上传文件大小限制:最小值,单位Byte
116123
/// </summary>

‎src/Qiniu/Util/Signature.cs

+45
Original file line numberDiff line numberDiff line change
@@ -200,5 +200,50 @@ public string SignRequestV2(string method, string url, StringDictionary headers,
200200
{
201201
return SignRequestV2(method, url, headers, Encoding.UTF8.GetString(body));
202202
}
203+
204+
public bool VerifyRequest(
205+
string method,
206+
string url,
207+
StringDictionary headers,
208+
string body = null
209+
)
210+
{
211+
byte[] bodyBytes = null;
212+
if (!string.IsNullOrEmpty(body)) {
213+
bodyBytes = Encoding.UTF8.GetBytes(body);
214+
}
215+
return VerifyRequest(
216+
method,
217+
url,
218+
headers,
219+
bodyBytes
220+
);
221+
}
222+
223+
public bool VerifyRequest(
224+
string method,
225+
string url,
226+
StringDictionary headers,
227+
byte[] body = null
228+
)
229+
{
230+
if (!headers.ContainsKey("Authorization"))
231+
{
232+
return false;
233+
}
234+
235+
string authString = headers["Authorization"];
236+
if (authString.StartsWith("QBox "))
237+
{
238+
return authString == "QBox " + SignRequest(url, body);
239+
}
240+
241+
if (authString.StartsWith("Qiniu "))
242+
{
243+
return authString == "Qiniu " + SignRequestV2(method, url, headers, body);
244+
}
245+
246+
return false;
247+
}
203248
}
204249
}

‎src/QiniuTests/Storage/ConfigTests.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ public void UcHostTest()
1111
{
1212
Config config = new Config();
1313
string ucHost = config.UcHost();
14-
Assert.AreEqual("http://uc.qbox.me", ucHost);
14+
Assert.AreEqual("http://uc.qiniuapi.com", ucHost);
1515
config.SetUcHost("uc.example.com");
1616
ucHost = config.UcHost();
1717
Assert.AreEqual("http://uc.example.com", ucHost);
1818

1919
config = new Config();
2020
config.UseHttps = true;
2121
ucHost = config.UcHost();
22-
Assert.AreEqual("https://uc.qbox.me", ucHost);
22+
Assert.AreEqual("https://uc.qiniuapi.com", ucHost);
2323
config.SetUcHost("uc.example.com");
2424
ucHost = config.UcHost();
2525
Assert.AreEqual("https://uc.example.com", ucHost);

‎src/QiniuTests/Storage/FormUploaderTests.cs

+51
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using NUnit.Framework;
22
using Qiniu.Http;
33
using System;
4+
using System.Collections.Generic;
5+
using Newtonsoft.Json;
46
using Qiniu.Util;
57
using Qiniu.Tests;
68

@@ -80,5 +82,54 @@ public void UploadFileV2Test()
8082
System.IO.File.Delete(filePath);
8183
}
8284

85+
[Test]
86+
public void UploadFileWithPersistTypeTest()
87+
{
88+
Mac mac = new Mac(AccessKey, SecretKey);
89+
Random rand = new Random();
90+
string key = string.Format("UploadFileTest_{0}.dat", rand.Next());
91+
92+
string tempPath = System.IO.Path.GetTempPath();
93+
int rnd = new Random().Next(1, 100000);
94+
string filePath = tempPath + "resumeFile" + rnd.ToString();
95+
char[] testBody = new char[4 * 1024 * 1024];
96+
System.IO.FileStream stream = new System.IO.FileStream(filePath, System.IO.FileMode.Create);
97+
System.IO.StreamWriter sw = new System.IO.StreamWriter(stream, System.Text.Encoding.Default);
98+
sw.Write(testBody);
99+
sw.Close();
100+
stream.Close();
101+
102+
PutPolicy putPolicy = new PutPolicy();
103+
putPolicy.Scope = Bucket + ":" + key;
104+
putPolicy.SetExpires(3600);
105+
putPolicy.DeleteAfterDays = 1;
106+
string saveEntry = Base64.UrlSafeBase64Encode(Bucket + ":pfop-test_avinfo");
107+
putPolicy.PersistentOps = "avinfo|saveas/" + saveEntry;
108+
putPolicy.PersistentType = 1;
109+
string token = Auth.CreateUploadToken(mac, putPolicy.ToJsonString());
110+
Config config = new Config();
111+
config.Zone = Zone.ZONE_CN_East;
112+
config.UseHttps = true;
113+
config.UseCdnDomains = true;
114+
FormUploader target = new FormUploader(config);
115+
PutExtra extra = new PutExtra();
116+
extra.Version = "v2";
117+
HttpResult result = target.UploadFile(filePath, key, token, extra);
118+
Console.WriteLine("form upload result: " + result.ToString());
119+
Assert.AreEqual((int)HttpCode.OK, result.Code);
120+
System.IO.File.Delete(filePath);
121+
122+
Dictionary<string, object> dict = JsonConvert.DeserializeObject<Dictionary<string, object>>(result.Text.ToString());
123+
Assert.IsTrue(dict.ContainsKey("persistentId"));
124+
OperationManager manager = new OperationManager(mac, config);
125+
PrefopResult prefopRet = manager.Prefop(dict["persistentId"].ToString());
126+
if (prefopRet.Code != (int)HttpCode.OK)
127+
{
128+
Assert.Fail("prefop error: " + prefopRet.ToString());
129+
}
130+
Assert.AreEqual(1, prefopRet.Result.Type);
131+
Assert.IsNotNull(prefopRet.Result.CreationDate);
132+
Assert.IsNotEmpty(prefopRet.Result.CreationDate);
133+
}
83134
}
84135
}

‎src/QiniuTests/Storage/OperationManagerTests.cs

+43-9
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,30 @@ namespace Qiniu.Storage.Tests
1111
[TestFixture]
1212
public class OperationManagerTests :TestEnv
1313
{
14+
private OperationManager getOperationManager()
15+
{
16+
Mac mac = new Mac(AccessKey, SecretKey);
17+
Config config = new Config();
18+
// config.UseHttps = true;
19+
20+
OperationManager manager = new OperationManager(mac, config);
21+
return manager;
22+
}
1423

1524
[Test]
16-
public void PfopTest()
25+
public void PfopAndPrefopTest()
1726
{
27+
string key = "qiniu.mp4";
28+
bool force = true;
29+
string pipeline = "sdktest";
30+
string notifyUrl = "http://api.example.com/qiniu/pfop/notify";
1831
string saveMp4Entry = Base64.UrlSafeBase64Encode(Bucket + ":avthumb_test_target.mp4");
1932
string saveJpgEntry = Base64.UrlSafeBase64Encode(Bucket + ":vframe_test_target.jpg");
2033
string avthumbMp4Fop = "avthumb/mp4|saveas/" + saveMp4Entry;
2134
string vframeJpgFop = "vframe/jpg/offset/1|saveas/" + saveJpgEntry;
2235
string fops = string.Join(";", new string[] { avthumbMp4Fop, vframeJpgFop });
23-
Mac mac = new Mac(AccessKey, SecretKey);
24-
Config config = new Config();
25-
config.UseHttps = true;
26-
OperationManager manager = new OperationManager(mac, config);
27-
string pipeline = "sdktest";
28-
string notifyUrl = "http://api.example.com/qiniu/pfop/notify";
29-
string key = "qiniu.mp4";
30-
bool force = true;
36+
37+
OperationManager manager = getOperationManager();
3138
PfopResult pfopRet = manager.Pfop(Bucket, key, fops, pipeline, notifyUrl, force);
3239
if (pfopRet.Code != (int)HttpCode.OK)
3340
{
@@ -42,5 +49,32 @@ public void PfopTest()
4249
}
4350
Console.WriteLine(ret.ToString());
4451
}
52+
53+
[Test]
54+
public void PfopWithIdleTimeTest()
55+
{
56+
string key = "qiniu.mp4";
57+
bool force = true;
58+
int type = 1;
59+
string pipeline = null;
60+
string saveJpgEntry = Base64.UrlSafeBase64Encode(Bucket + ":vframe_test_target.jpg");
61+
string vframeJpgFop = "vframe/jpg/offset/1|saveas/" + saveJpgEntry;
62+
63+
OperationManager manager = getOperationManager();
64+
PfopResult pfopRet = manager.Pfop(Bucket, key, vframeJpgFop, pipeline, null, force, type);
65+
if (pfopRet.Code != (int)HttpCode.OK)
66+
{
67+
Assert.Fail("pfop error: " + pfopRet.ToString());
68+
}
69+
70+
PrefopResult prefopRet = manager.Prefop(pfopRet.PersistentId);
71+
if (prefopRet.Code != (int)HttpCode.OK)
72+
{
73+
Assert.Fail("prefop error: " + prefopRet.ToString());
74+
}
75+
Assert.AreEqual(1, prefopRet.Result.Type);
76+
Assert.IsNotNull(prefopRet.Result.CreationDate);
77+
Assert.IsNotEmpty(prefopRet.Result.CreationDate);
78+
}
4579
}
4680
}

‎src/QiniuTests/Util/Signature.cs

+8
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,13 @@ public string SignatureV2ByBytesTest(string method, string url, StringDictionary
2323
{
2424
return string.Format("Qiniu {0}", sign.SignRequestV2(method, url, headers, Encoding.UTF8.GetBytes(body)));
2525
}
26+
27+
[TestCaseSource(typeof(VerifyRequestDataClass), nameof(VerifyRequestDataClass.TestCases))]
28+
public bool VerifyRequestTest(string method, string url, StringDictionary headers, string body)
29+
{
30+
Mac mac = new Mac("abcdefghklmnopq", "1234567890");
31+
Signature mockSign = new Signature(mac);
32+
return mockSign.VerifyRequest(method, url, headers, body);
33+
}
2634
}
2735
}

‎src/QiniuTests/Util/TestCases.cs

+33-1
Original file line numberDiff line numberDiff line change
@@ -211,4 +211,36 @@ public static IEnumerable TestCases
211211
}
212212
}
213213
}
214-
}
214+
215+
public class VerifyRequestDataClass
216+
{
217+
public static IEnumerable TestCases
218+
{
219+
get
220+
{
221+
yield return new TestCaseData(
222+
"",
223+
"https://test.qiniu.com/callback",
224+
new StringDictionary
225+
{
226+
{"Authorization", "QBox abcdefghklmnopq:T7F-SjxX7X2zI4Fc1vANiNt1AUE="},
227+
{"Content-Type", "application/x-www-form-urlencoded"}
228+
},
229+
"name=sunflower.jpg&hash=Fn6qeQi4VDLQ347NiRm-RlQx_4O2&location=Shanghai&price=1500.00&uid=123"
230+
).Returns(true);
231+
232+
yield return new TestCaseData(
233+
"GET",
234+
"https://test.qiniu.com/callback",
235+
new StringDictionary
236+
{
237+
{"Authorization", "Qiniu abcdefghklmnopq:ZqS7EZuAKrhZaEIxqNGxDJi41IQ="},
238+
{"X-Qiniu-Bbb", "BBB"},
239+
{"Content-Type", "application/x-www-form-urlencoded"}
240+
},
241+
"name=sunflower.jpg&hash=Fn6qeQi4VDLQ347NiRm-RlQx_4O2&location=Shanghai&price=1500.00&uid=123"
242+
).Returns(true);
243+
}
244+
}
245+
}
246+
}

0 commit comments

Comments
 (0)
Please sign in to comment.