Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bump to v8.6.0 #264

Merged
merged 5 commits into from
Sep 20, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
**2024-09-05**

v8.6.0

新增:验证 Qbox, Qiniu 签名的辅助方法

新增:持久化处理,支持闲时任务

更改:对象存储,默认空间管理域名,查询区域主备域名

**2024-08-23**

v8.5.1
2 changes: 1 addition & 1 deletion src/Qiniu/Qiniu.csproj
Original file line number Diff line number Diff line change
@@ -28,7 +28,7 @@
<PostBuildEvent>
</PostBuildEvent>
<ProjectId>Qiniu</ProjectId>
<Version>8.5.1</Version>
<Version>8.6.0</Version>
<Authors>Rong Zhou, Qiniu SDK</Authors>
<Company>Shanghai Qiniu Information Technology Co., Ltd.</Company>
<Description>Qiniu Resource (Cloud) Storage SDK for C#</Description>
2 changes: 1 addition & 1 deletion src/Qiniu/QiniuCSharpSDK.cs
Original file line number Diff line number Diff line change
@@ -37,6 +37,6 @@ public class QiniuCSharpSDK
/// <summary>
/// SDK版本号
/// </summary>
public const string VERSION = "8.5.1";
public const string VERSION = "8.6.0";

}
8 changes: 4 additions & 4 deletions src/Qiniu/Storage/Config.cs
Original file line number Diff line number Diff line change
@@ -14,18 +14,18 @@ public class Config
/// <summary>
/// 默认空间管理域名
/// </summary>
public static string DefaultUcHost = "uc.qbox.me";
public static string DefaultUcHost = "uc.qiniuapi.com";
/// <summary>
/// 默认查询区域域名
/// </summary>
public static string DefaultQueryRegionHost = "kodo-config.qiniuapi.com";
public static string DefaultQueryRegionHost = "uc.qiniuapi.com";
/// <summary>
/// 默认备用查询区域域名
/// </summary>
public static List<string> DefaultBackupQueryRegionHosts = new List<string>
{
"uc.qbox.me",
"api.qiniu.com"
"kodo-config.qiniuapi.com",
"uc.qbox.me"
};

/// <summary>
15 changes: 14 additions & 1 deletion src/Qiniu/Storage/OperationManager.cs
Original file line number Diff line number Diff line change
@@ -41,8 +41,17 @@ public OperationManager(Mac mac, Config config)
/// <param name="pipeline">私有队列</param>
/// <param name="notifyUrl">通知url</param>
/// <param name="force">forece参数</param>
/// <param name="persistentType">为 1 时开启闲时任务</param>
/// <returns>pfop操作返回结果,正确返回结果包含persistentId</returns>
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);

10 changes: 10 additions & 0 deletions src/Qiniu/Storage/PfopInfo.cs
Original file line number Diff line number Diff line change
@@ -13,6 +13,16 @@ public class PfopInfo
[JsonProperty("id")]
public string Id;
/// <summary>
/// 任务类型,为 1 代表为闲时任务
/// </summary>
[JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
public int? Type;
/// <summary>
/// 任务创建时间
/// </summary>
[JsonProperty("creationDate", NullValueHandling = NullValueHandling.Ignore)]
public string CreationDate;
/// <summary>
/// 任务结果状态码
/// </summary>
[JsonProperty("code")]
2 changes: 1 addition & 1 deletion src/Qiniu/Storage/PrefopResult.cs
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ public PfopInfo Result

if ((Code == (int)HttpCode.OK) && (!string.IsNullOrEmpty(Text)))
{
info= JsonConvert.DeserializeObject<PfopInfo>(Text);
info= JsonConvert.DeserializeObject<PfopInfo>(Text);
}
return info;
}
7 changes: 7 additions & 0 deletions src/Qiniu/Storage/PutPolicy.cs
Original file line number Diff line number Diff line change
@@ -111,6 +111,13 @@ public class PutPolicy
[JsonProperty("persistentPipeline", NullValueHandling = NullValueHandling.Ignore)]
public string PersistentPipeline { get; set; }

/// <summary>
/// [可选]持久化任务类型,为 1 时开启闲时任务
/// </summary>
[JsonProperty("persistentType", NullValueHandling = NullValueHandling.Ignore)]
public int? PersistentType { get; set; }


/// <summary>
/// [可选]上传文件大小限制:最小值,单位Byte
/// </summary>
45 changes: 45 additions & 0 deletions src/Qiniu/Util/Signature.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
4 changes: 2 additions & 2 deletions src/QiniuTests/Storage/ConfigTests.cs
Original file line number Diff line number Diff line change
@@ -11,15 +11,15 @@ 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);

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);
51 changes: 51 additions & 0 deletions src/QiniuTests/Storage/FormUploaderTests.cs
Original file line number Diff line number Diff line change
@@ -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<string, object> dict = JsonConvert.DeserializeObject<Dictionary<string, object>>(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);
}
}
}
52 changes: 43 additions & 9 deletions src/QiniuTests/Storage/OperationManagerTests.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
8 changes: 8 additions & 0 deletions src/QiniuTests/Util/Signature.cs
Original file line number Diff line number Diff line change
@@ -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);
}
}
}
34 changes: 33 additions & 1 deletion src/QiniuTests/Util/TestCases.cs
Original file line number Diff line number Diff line change
@@ -211,4 +211,36 @@ public static IEnumerable TestCases
}
}
}
}

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);
}
}
}
}