diff --git a/CHANGELOG.md b/CHANGELOG.md
index cca2f38..1ad8f3b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,13 @@
+**2024-09-05**
+
+v8.6.0
+
+新增:验证 Qbox, Qiniu 签名的辅助方法
+
+新增:持久化处理,支持闲时任务
+
+更改:对象存储,默认空间管理域名,查询区域主备域名
+
**2024-08-23**
v8.5.1
diff --git a/src/Qiniu/Qiniu.csproj b/src/Qiniu/Qiniu.csproj
index a166e1a..bd2b8fd 100644
--- a/src/Qiniu/Qiniu.csproj
+++ b/src/Qiniu/Qiniu.csproj
@@ -28,7 +28,7 @@
Qiniu
- 8.5.1
+ 8.6.0
Rong Zhou, Qiniu SDK
Shanghai Qiniu Information Technology Co., Ltd.
Qiniu Resource (Cloud) Storage SDK for C#
diff --git a/src/Qiniu/QiniuCSharpSDK.cs b/src/Qiniu/QiniuCSharpSDK.cs
index 65e023a..eef1201 100644
--- a/src/Qiniu/QiniuCSharpSDK.cs
+++ b/src/Qiniu/QiniuCSharpSDK.cs
@@ -37,6 +37,6 @@ public class QiniuCSharpSDK
///
/// SDK版本号
///
- public const string VERSION = "8.5.1";
+ public const string VERSION = "8.6.0";
}
diff --git a/src/Qiniu/Storage/Config.cs b/src/Qiniu/Storage/Config.cs
index ecbb803..949f4e9 100644
--- a/src/Qiniu/Storage/Config.cs
+++ b/src/Qiniu/Storage/Config.cs
@@ -14,18 +14,18 @@ public class Config
///
/// 默认空间管理域名
///
- public static string DefaultUcHost = "uc.qbox.me";
+ public static string DefaultUcHost = "uc.qiniuapi.com";
///
/// 默认查询区域域名
///
- public static string DefaultQueryRegionHost = "kodo-config.qiniuapi.com";
+ public static string DefaultQueryRegionHost = "uc.qiniuapi.com";
///
/// 默认备用查询区域域名
///
public static List DefaultBackupQueryRegionHosts = new List
{
- "uc.qbox.me",
- "api.qiniu.com"
+ "kodo-config.qiniuapi.com",
+ "uc.qbox.me"
};
///
diff --git a/src/Qiniu/Storage/OperationManager.cs b/src/Qiniu/Storage/OperationManager.cs
index 8a3b8dd..07bac22 100644
--- a/src/Qiniu/Storage/OperationManager.cs
+++ b/src/Qiniu/Storage/OperationManager.cs
@@ -41,8 +41,17 @@ public OperationManager(Mac mac, Config config)
/// 私有队列
/// 通知url
/// forece参数
+ /// 为 1 时开启闲时任务
/// pfop操作返回结果,正确返回结果包含persistentId
- public PfopResult Pfop(string bucket, string key, string fops, string pipeline, string notifyUrl, bool force)
+ public PfopResult Pfop(
+ string bucket,
+ string key,
+ string fops,
+ string pipeline,
+ string notifyUrl,
+ bool force,
+ int type = 0
+ )
{
PfopResult result = new PfopResult();
@@ -65,6 +74,10 @@ public PfopResult Pfop(string bucket, string key, string fops, string pipeline,
{
sb.AppendFormat("&pipeline={0}", pipeline);
}
+ if (type > 0)
+ {
+ sb.AppendFormat("&type={0}", type);
+ }
byte[] data = Encoding.UTF8.GetBytes(sb.ToString());
string token = auth.CreateManageToken(pfopUrl, data);
diff --git a/src/Qiniu/Storage/PfopInfo.cs b/src/Qiniu/Storage/PfopInfo.cs
index f3a95e3..2dfbd0a 100644
--- a/src/Qiniu/Storage/PfopInfo.cs
+++ b/src/Qiniu/Storage/PfopInfo.cs
@@ -13,6 +13,16 @@ public class PfopInfo
[JsonProperty("id")]
public string Id;
///
+ /// 任务类型,为 1 代表为闲时任务
+ ///
+ [JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
+ public int? Type;
+ ///
+ /// 任务创建时间
+ ///
+ [JsonProperty("creationDate", NullValueHandling = NullValueHandling.Ignore)]
+ public string CreationDate;
+ ///
/// 任务结果状态码
///
[JsonProperty("code")]
diff --git a/src/Qiniu/Storage/PrefopResult.cs b/src/Qiniu/Storage/PrefopResult.cs
index bd248bd..2b5a733 100644
--- a/src/Qiniu/Storage/PrefopResult.cs
+++ b/src/Qiniu/Storage/PrefopResult.cs
@@ -19,7 +19,7 @@ public PfopInfo Result
if ((Code == (int)HttpCode.OK) && (!string.IsNullOrEmpty(Text)))
{
- info= JsonConvert.DeserializeObject(Text);
+ info= JsonConvert.DeserializeObject(Text);
}
return info;
}
diff --git a/src/Qiniu/Storage/PutPolicy.cs b/src/Qiniu/Storage/PutPolicy.cs
index 79be623..715ef64 100644
--- a/src/Qiniu/Storage/PutPolicy.cs
+++ b/src/Qiniu/Storage/PutPolicy.cs
@@ -111,6 +111,13 @@ public class PutPolicy
[JsonProperty("persistentPipeline", NullValueHandling = NullValueHandling.Ignore)]
public string PersistentPipeline { get; set; }
+ ///
+ /// [可选]持久化任务类型,为 1 时开启闲时任务
+ ///
+ [JsonProperty("persistentType", NullValueHandling = NullValueHandling.Ignore)]
+ public int? PersistentType { get; set; }
+
+
///
/// [可选]上传文件大小限制:最小值,单位Byte
///
diff --git a/src/Qiniu/Util/Signature.cs b/src/Qiniu/Util/Signature.cs
index 02c2aad..915cb65 100644
--- a/src/Qiniu/Util/Signature.cs
+++ b/src/Qiniu/Util/Signature.cs
@@ -200,5 +200,50 @@ public string SignRequestV2(string method, string url, StringDictionary headers,
{
return SignRequestV2(method, url, headers, Encoding.UTF8.GetString(body));
}
+
+ public bool VerifyRequest(
+ string method,
+ string url,
+ StringDictionary headers,
+ string body = null
+ )
+ {
+ byte[] bodyBytes = null;
+ if (!string.IsNullOrEmpty(body)) {
+ bodyBytes = Encoding.UTF8.GetBytes(body);
+ }
+ return VerifyRequest(
+ method,
+ url,
+ headers,
+ bodyBytes
+ );
+ }
+
+ public bool VerifyRequest(
+ string method,
+ string url,
+ StringDictionary headers,
+ byte[] body = null
+ )
+ {
+ if (!headers.ContainsKey("Authorization"))
+ {
+ return false;
+ }
+
+ string authString = headers["Authorization"];
+ if (authString.StartsWith("QBox "))
+ {
+ return authString == "QBox " + SignRequest(url, body);
+ }
+
+ if (authString.StartsWith("Qiniu "))
+ {
+ return authString == "Qiniu " + SignRequestV2(method, url, headers, body);
+ }
+
+ return false;
+ }
}
}
diff --git a/src/QiniuTests/Storage/ConfigTests.cs b/src/QiniuTests/Storage/ConfigTests.cs
index 8ea1f67..1215a15 100644
--- a/src/QiniuTests/Storage/ConfigTests.cs
+++ b/src/QiniuTests/Storage/ConfigTests.cs
@@ -11,7 +11,7 @@ public void UcHostTest()
{
Config config = new Config();
string ucHost = config.UcHost();
- Assert.AreEqual("http://uc.qbox.me", ucHost);
+ Assert.AreEqual("http://uc.qiniuapi.com", ucHost);
config.SetUcHost("uc.example.com");
ucHost = config.UcHost();
Assert.AreEqual("http://uc.example.com", ucHost);
@@ -19,7 +19,7 @@ public void UcHostTest()
config = new Config();
config.UseHttps = true;
ucHost = config.UcHost();
- Assert.AreEqual("https://uc.qbox.me", ucHost);
+ Assert.AreEqual("https://uc.qiniuapi.com", ucHost);
config.SetUcHost("uc.example.com");
ucHost = config.UcHost();
Assert.AreEqual("https://uc.example.com", ucHost);
diff --git a/src/QiniuTests/Storage/FormUploaderTests.cs b/src/QiniuTests/Storage/FormUploaderTests.cs
index 0eb2a6d..bb145ec 100644
--- a/src/QiniuTests/Storage/FormUploaderTests.cs
+++ b/src/QiniuTests/Storage/FormUploaderTests.cs
@@ -1,6 +1,8 @@
using NUnit.Framework;
using Qiniu.Http;
using System;
+using System.Collections.Generic;
+using Newtonsoft.Json;
using Qiniu.Util;
using Qiniu.Tests;
@@ -80,5 +82,54 @@ public void UploadFileV2Test()
System.IO.File.Delete(filePath);
}
+ [Test]
+ public void UploadFileWithPersistTypeTest()
+ {
+ Mac mac = new Mac(AccessKey, SecretKey);
+ Random rand = new Random();
+ string key = string.Format("UploadFileTest_{0}.dat", rand.Next());
+
+ string tempPath = System.IO.Path.GetTempPath();
+ int rnd = new Random().Next(1, 100000);
+ string filePath = tempPath + "resumeFile" + rnd.ToString();
+ char[] testBody = new char[4 * 1024 * 1024];
+ System.IO.FileStream stream = new System.IO.FileStream(filePath, System.IO.FileMode.Create);
+ System.IO.StreamWriter sw = new System.IO.StreamWriter(stream, System.Text.Encoding.Default);
+ sw.Write(testBody);
+ sw.Close();
+ stream.Close();
+
+ PutPolicy putPolicy = new PutPolicy();
+ putPolicy.Scope = Bucket + ":" + key;
+ putPolicy.SetExpires(3600);
+ putPolicy.DeleteAfterDays = 1;
+ string saveEntry = Base64.UrlSafeBase64Encode(Bucket + ":pfop-test_avinfo");
+ putPolicy.PersistentOps = "avinfo|saveas/" + saveEntry;
+ putPolicy.PersistentType = 1;
+ string token = Auth.CreateUploadToken(mac, putPolicy.ToJsonString());
+ Config config = new Config();
+ config.Zone = Zone.ZONE_CN_East;
+ config.UseHttps = true;
+ config.UseCdnDomains = true;
+ FormUploader target = new FormUploader(config);
+ PutExtra extra = new PutExtra();
+ extra.Version = "v2";
+ HttpResult result = target.UploadFile(filePath, key, token, extra);
+ Console.WriteLine("form upload result: " + result.ToString());
+ Assert.AreEqual((int)HttpCode.OK, result.Code);
+ System.IO.File.Delete(filePath);
+
+ Dictionary dict = JsonConvert.DeserializeObject>(result.Text.ToString());
+ Assert.IsTrue(dict.ContainsKey("persistentId"));
+ OperationManager manager = new OperationManager(mac, config);
+ PrefopResult prefopRet = manager.Prefop(dict["persistentId"].ToString());
+ if (prefopRet.Code != (int)HttpCode.OK)
+ {
+ Assert.Fail("prefop error: " + prefopRet.ToString());
+ }
+ Assert.AreEqual(1, prefopRet.Result.Type);
+ Assert.IsNotNull(prefopRet.Result.CreationDate);
+ Assert.IsNotEmpty(prefopRet.Result.CreationDate);
+ }
}
}
diff --git a/src/QiniuTests/Storage/OperationManagerTests.cs b/src/QiniuTests/Storage/OperationManagerTests.cs
index 6b26175..3150676 100644
--- a/src/QiniuTests/Storage/OperationManagerTests.cs
+++ b/src/QiniuTests/Storage/OperationManagerTests.cs
@@ -11,23 +11,30 @@ namespace Qiniu.Storage.Tests
[TestFixture]
public class OperationManagerTests :TestEnv
{
+ private OperationManager getOperationManager()
+ {
+ Mac mac = new Mac(AccessKey, SecretKey);
+ Config config = new Config();
+ // config.UseHttps = true;
+
+ OperationManager manager = new OperationManager(mac, config);
+ return manager;
+ }
[Test]
- public void PfopTest()
+ public void PfopAndPrefopTest()
{
+ string key = "qiniu.mp4";
+ bool force = true;
+ string pipeline = "sdktest";
+ string notifyUrl = "http://api.example.com/qiniu/pfop/notify";
string saveMp4Entry = Base64.UrlSafeBase64Encode(Bucket + ":avthumb_test_target.mp4");
string saveJpgEntry = Base64.UrlSafeBase64Encode(Bucket + ":vframe_test_target.jpg");
string avthumbMp4Fop = "avthumb/mp4|saveas/" + saveMp4Entry;
string vframeJpgFop = "vframe/jpg/offset/1|saveas/" + saveJpgEntry;
string fops = string.Join(";", new string[] { avthumbMp4Fop, vframeJpgFop });
- Mac mac = new Mac(AccessKey, SecretKey);
- Config config = new Config();
- config.UseHttps = true;
- OperationManager manager = new OperationManager(mac, config);
- string pipeline = "sdktest";
- string notifyUrl = "http://api.example.com/qiniu/pfop/notify";
- string key = "qiniu.mp4";
- bool force = true;
+
+ OperationManager manager = getOperationManager();
PfopResult pfopRet = manager.Pfop(Bucket, key, fops, pipeline, notifyUrl, force);
if (pfopRet.Code != (int)HttpCode.OK)
{
@@ -42,5 +49,32 @@ public void PfopTest()
}
Console.WriteLine(ret.ToString());
}
+
+ [Test]
+ public void PfopWithIdleTimeTest()
+ {
+ string key = "qiniu.mp4";
+ bool force = true;
+ int type = 1;
+ string pipeline = null;
+ string saveJpgEntry = Base64.UrlSafeBase64Encode(Bucket + ":vframe_test_target.jpg");
+ string vframeJpgFop = "vframe/jpg/offset/1|saveas/" + saveJpgEntry;
+
+ OperationManager manager = getOperationManager();
+ PfopResult pfopRet = manager.Pfop(Bucket, key, vframeJpgFop, pipeline, null, force, type);
+ if (pfopRet.Code != (int)HttpCode.OK)
+ {
+ Assert.Fail("pfop error: " + pfopRet.ToString());
+ }
+
+ PrefopResult prefopRet = manager.Prefop(pfopRet.PersistentId);
+ if (prefopRet.Code != (int)HttpCode.OK)
+ {
+ Assert.Fail("prefop error: " + prefopRet.ToString());
+ }
+ Assert.AreEqual(1, prefopRet.Result.Type);
+ Assert.IsNotNull(prefopRet.Result.CreationDate);
+ Assert.IsNotEmpty(prefopRet.Result.CreationDate);
+ }
}
}
diff --git a/src/QiniuTests/Util/Signature.cs b/src/QiniuTests/Util/Signature.cs
index ae5e501..2bbfa75 100644
--- a/src/QiniuTests/Util/Signature.cs
+++ b/src/QiniuTests/Util/Signature.cs
@@ -23,5 +23,13 @@ public string SignatureV2ByBytesTest(string method, string url, StringDictionary
{
return string.Format("Qiniu {0}", sign.SignRequestV2(method, url, headers, Encoding.UTF8.GetBytes(body)));
}
+
+ [TestCaseSource(typeof(VerifyRequestDataClass), nameof(VerifyRequestDataClass.TestCases))]
+ public bool VerifyRequestTest(string method, string url, StringDictionary headers, string body)
+ {
+ Mac mac = new Mac("abcdefghklmnopq", "1234567890");
+ Signature mockSign = new Signature(mac);
+ return mockSign.VerifyRequest(method, url, headers, body);
+ }
}
}
\ No newline at end of file
diff --git a/src/QiniuTests/Util/TestCases.cs b/src/QiniuTests/Util/TestCases.cs
index 3a52b62..1914979 100644
--- a/src/QiniuTests/Util/TestCases.cs
+++ b/src/QiniuTests/Util/TestCases.cs
@@ -211,4 +211,36 @@ public static IEnumerable TestCases
}
}
}
-}
\ No newline at end of file
+
+ public class VerifyRequestDataClass
+ {
+ public static IEnumerable TestCases
+ {
+ get
+ {
+ yield return new TestCaseData(
+ "",
+ "https://test.qiniu.com/callback",
+ new StringDictionary
+ {
+ {"Authorization", "QBox abcdefghklmnopq:T7F-SjxX7X2zI4Fc1vANiNt1AUE="},
+ {"Content-Type", "application/x-www-form-urlencoded"}
+ },
+ "name=sunflower.jpg&hash=Fn6qeQi4VDLQ347NiRm-RlQx_4O2&location=Shanghai&price=1500.00&uid=123"
+ ).Returns(true);
+
+ yield return new TestCaseData(
+ "GET",
+ "https://test.qiniu.com/callback",
+ new StringDictionary
+ {
+ {"Authorization", "Qiniu abcdefghklmnopq:ZqS7EZuAKrhZaEIxqNGxDJi41IQ="},
+ {"X-Qiniu-Bbb", "BBB"},
+ {"Content-Type", "application/x-www-form-urlencoded"}
+ },
+ "name=sunflower.jpg&hash=Fn6qeQi4VDLQ347NiRm-RlQx_4O2&location=Shanghai&price=1500.00&uid=123"
+ ).Returns(true);
+ }
+ }
+ }
+}