diff --git a/Directory.Packages.props b/Directory.Packages.props
index b63cc5e..d8a7f94 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -21,5 +21,6 @@
+
diff --git a/sample/MultipartUpload/MultipartUpload.csproj b/sample/MultipartUpload/MultipartUpload.csproj
new file mode 100644
index 0000000..543d04c
--- /dev/null
+++ b/sample/MultipartUpload/MultipartUpload.csproj
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/sample/MultipartUpload/Program.cs b/sample/MultipartUpload/Program.cs
new file mode 100644
index 0000000..ad10d9c
--- /dev/null
+++ b/sample/MultipartUpload/Program.cs
@@ -0,0 +1,109 @@
+using CommandLine;
+using OSS = AlibabaCloud.OSS.V2;
+
+namespace Sample.MultipartUpload
+{
+ public class Program
+ {
+
+ public class Options
+ {
+ [Option("region", Required = true, HelpText = "The region in which the bucket is located.")]
+ public string? Region { get; set; }
+
+ [Option("endpoint", Required = false, HelpText = "The domain names that other services can use to access OSS.")]
+ public string? Endpoint { get; set; }
+
+ [Option("bucket", Required = true, HelpText = "The `name` of the bucket.")]
+ public string? Bucket { get; set; }
+
+ [Option("key", Required = true, HelpText = "The `name` of the object.")]
+ public string? Key { get; set; }
+
+ [Option("partsize", HelpText = "The part size, 512*1024 as default.")]
+ public long? PartSize { get; set; }
+
+ [Option("filepath", Required = true, HelpText = "The path of a file to upload.")]
+ public string? FilePath { get; set; }
+ }
+
+ public static async Task Main(string[] args)
+ {
+
+ var parserResult = Parser.Default.ParseArguments(args);
+ if (parserResult.Errors.Any())
+ {
+ Environment.Exit(1);
+ }
+ var option = parserResult.Value;
+
+ // Specify the region and other parameters.
+ var region = option.Region;
+ var bucket = option.Bucket;
+ var endpoint = option.Endpoint;
+ var key = option.Key;
+ var filePath = option.FilePath!;
+ long partSize = option.PartSize ?? 512*1024;
+
+ // Using the SDK's default configuration
+ // loading credentials values from the environment variables
+ var cfg = OSS.Configuration.LoadDefault();
+ cfg.CredentialsProvider = new OSS.Credentials.EnvironmentVariableCredentialsProvider();
+ cfg.Region = region;
+
+ if (endpoint != null)
+ {
+ cfg.Endpoint = endpoint;
+ }
+
+ using var client = new OSS.Client(cfg);
+
+ var initResult = await client.InitiateMultipartUploadAsync(new()
+ {
+ Bucket = bucket,
+ Key = key
+ });
+
+ // upload
+ using var file = File.OpenRead(filePath);
+ long fileSize = file.Length;
+ long partNumber = 1;
+
+ var uploadParts = new List();
+
+ for (long offset = 0; offset < fileSize; offset += partSize)
+ {
+ var size = Math.Min(partSize, fileSize - offset);
+ var upResult = await client.UploadPartAsync(new()
+ {
+ Bucket = bucket,
+ Key = key,
+ PartNumber = partNumber,
+ UploadId = initResult.UploadId,
+ Body = new OSS.IO.BoundedStream(file, offset, size)
+ });
+ uploadParts.Add(new () { PartNumber = partNumber, ETag = upResult.ETag });
+ partNumber++;
+ }
+
+ // complete
+ uploadParts.Sort((left, right) => { return (left.PartNumber > right.PartNumber) ? 1 : -1; });
+ var cmResult = await client.CompleteMultipartUploadAsync(new()
+ {
+ Bucket = bucket,
+ Key = key,
+ UploadId = initResult.UploadId,
+ CompleteMultipartUpload = new ()
+ {
+ Parts = uploadParts
+ }
+ });
+
+ Console.WriteLine("MultipartUpload done");
+ Console.WriteLine($"StatusCode: {cmResult.StatusCode}");
+ Console.WriteLine($"RequestId: {cmResult.RequestId}");
+ Console.WriteLine("Response Headers:");
+ cmResult.Headers.ToList().ForEach(x => Console.WriteLine(x.Key + " : " + x.Value));
+ }
+ }
+}
diff --git a/sample/PostObject/PostObject.csproj b/sample/PostObject/PostObject.csproj
new file mode 100644
index 0000000..e11f9aa
--- /dev/null
+++ b/sample/PostObject/PostObject.csproj
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sample/PostObject/Program.cs b/sample/PostObject/Program.cs
new file mode 100644
index 0000000..3bf863f
--- /dev/null
+++ b/sample/PostObject/Program.cs
@@ -0,0 +1,145 @@
+using System.Globalization;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Security.Cryptography;
+using System.Text;
+using System.Text.Json;
+using CommandLine;
+using OSS = AlibabaCloud.OSS.V2;
+
+namespace Sample.PostObject
+{
+ public class Program
+ {
+
+ public class Options
+ {
+ [Option("region", Required = true, HelpText = "The region in which the bucket is located.")]
+ public string? Region { get; set; }
+
+ [Option("bucket", Required = true, HelpText = "The `name` of the bucket.")]
+ public string? Bucket { get; set; }
+
+ [Option("key", Required = true, HelpText = "The `name` of the object.")]
+ public string? Key { get; set; }
+ }
+
+ static string ToHexString(byte[] data, bool lowercase)
+ {
+ var sb = new StringBuilder();
+ for (var i = 0; i < data.Length; i++) sb.Append(data[i].ToString(lowercase ? "x2" : "X2"));
+ return sb.ToString();
+ }
+
+ static string quote(string value)
+ {
+ return $"\"{value}\"";
+ }
+
+ public static async Task Main(string[] args)
+ {
+
+ var parserResult = Parser.Default.ParseArguments(args);
+ if (parserResult.Errors.Any())
+ {
+ Environment.Exit(1);
+ }
+ var option = parserResult.Value;
+
+ // Specify the region and other parameters.
+ var region = option.Region!;
+ var bucket = option.Bucket!;
+ var key = option.Key!;
+ var product = "oss";
+
+ // loading credentials values from the environment variables
+ var credentialsProvider = new OSS.Credentials.EnvironmentVariableCredentialsProvider();
+ var credentials = credentialsProvider.GetCredentials();
+
+ var content = "hi oss";
+
+ // build policy
+ var utcTime = DateTime.UtcNow;
+ var date = utcTime.ToUniversalTime().ToString("yyyyMMdd", CultureInfo.InvariantCulture);
+ var dateTime = utcTime.ToUniversalTime().ToString("yyyyMMdd'T'HHmmss'Z'", CultureInfo.InvariantCulture);
+ var expiration = utcTime.AddHours(1);
+ var credentialInfo = $"{credentials.AccessKeyId}/{date}/{region}/{product}/aliyun_v4_request";
+ var policyMap = new Dictionary()
+ {
+ { "expiration",
+ expiration.ToUniversalTime().ToString("yyyy-MM-dd'T'HH:mm:ss.000'Z'", CultureInfo.InvariantCulture)
+ },
+ { "conditions", new Object[]{
+ new Dictionary() {{ "bucket", bucket } },
+ new Dictionary() {{ "x-oss-signature-version", "OSS4-HMAC-SHA256" } },
+ new Dictionary() {{ "x-oss-credential", credentialInfo } },
+ new Dictionary() {{ "x-oss-date", dateTime } },
+ //other condition
+ new Object[]{"content-length-range", 1, 1024 },
+ //new Object[]{"eq", "$success_action_status", "201"},
+ //new Object[]{"starts-with", "$key", "user/eric/"},
+ //new Object[]{"in", "$content-type", new string[]{"image/jpg", "image/png"}},
+ //new Object[]{ "not-in", "$cache-control", new string[]{ "no-cache" } },
+ }
+ },
+ };
+
+ var policy = JsonSerializer.Serialize(policyMap);
+
+ // sign policy
+ var stringToSign = Convert.ToBase64String(Encoding.UTF8.GetBytes(policy));
+
+ // signing key
+ using var kha = new HMACSHA256();
+
+ var ksecret = Encoding.UTF8.GetBytes("aliyun_v4" + credentials.AccessKeySecret);
+
+ kha.Key = ksecret;
+ var hashDate = kha.ComputeHash(Encoding.UTF8.GetBytes(date));
+
+ kha.Key = hashDate;
+ var hashRegion = kha.ComputeHash(Encoding.UTF8.GetBytes(region));
+
+ kha.Key = hashRegion;
+ var hashProduct = kha.ComputeHash(Encoding.UTF8.GetBytes(product));
+
+ kha.Key = hashProduct;
+ var signingKey = kha.ComputeHash(Encoding.UTF8.GetBytes("aliyun_v4_request"));
+
+ // Signature
+ kha.Key = signingKey;
+ var signature = ToHexString(kha.ComputeHash(Encoding.UTF8.GetBytes(stringToSign)), true);
+
+ // Multipar form
+ using var formData = new MultipartFormDataContent();
+ // trim quote
+ var boundary = formData.Headers.ContentType!.Parameters.ElementAt(0).Value!;
+ formData.Headers.ContentType.Parameters.ElementAt(0).Value = boundary.Trim('"');
+
+ // object info, key & metadata
+ formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes(key)), quote("key"));
+ // meta-data
+ //formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes(value)), quote("x-oss-"));
+ // policy
+ formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes(stringToSign)), quote("policy"));
+ // Signature
+ formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes("OSS4-HMAC-SHA256")), quote("x-oss-signature-version"));
+ formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes(credentialInfo)), quote("x-oss-credential"));
+ formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes(dateTime)), quote("x-oss-date"));
+ formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes(signature)), quote("x-oss-signature"));
+
+ // Data
+ formData.Add(new ByteArrayContent(Encoding.UTF8.GetBytes(content)), quote("file"));
+
+ // Send request
+ using var hc = new HttpClient();
+
+ var result = await hc.PostAsync($"http://{bucket}.oss-{region}.aliyuncs.com/", formData);
+
+ Console.WriteLine("PostObject done");
+ Console.WriteLine($"StatusCode: {result.StatusCode}");
+ Console.WriteLine("Response Headers:");
+ result.Headers.ToList().ForEach(x => Console.WriteLine(x.Key + " : " + String.Join(",", x.Value.ToList())));
+ }
+ }
+}
diff --git a/sample/PresignMultipartUpload/PresignMultipartUpload.csproj b/sample/PresignMultipartUpload/PresignMultipartUpload.csproj
new file mode 100644
index 0000000..543d04c
--- /dev/null
+++ b/sample/PresignMultipartUpload/PresignMultipartUpload.csproj
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
diff --git a/sample/PresignMultipartUpload/Program.cs b/sample/PresignMultipartUpload/Program.cs
new file mode 100644
index 0000000..e63e310
--- /dev/null
+++ b/sample/PresignMultipartUpload/Program.cs
@@ -0,0 +1,134 @@
+using System.Net.Http;
+using CommandLine;
+using OSS = AlibabaCloud.OSS.V2;
+
+namespace Sample.PresignMultipartUpload
+{
+ public class Program
+ {
+
+ public class Options
+ {
+ [Option("region", Required = true, HelpText = "The region in which the bucket is located.")]
+ public string? Region { get; set; }
+
+ [Option("endpoint", Required = false, HelpText = "The domain names that other services can use to access OSS.")]
+ public string? Endpoint { get; set; }
+
+ [Option("bucket", Required = true, HelpText = "The `name` of the bucket.")]
+ public string? Bucket { get; set; }
+
+ [Option("key", Required = true, HelpText = "The `name` of the object.")]
+ public string? Key { get; set; }
+
+ [Option("partsize", HelpText = "The part size, 512*1024 as default.")]
+ public long? PartSize { get; set; }
+
+ [Option("filepath", Required = true, HelpText = "The path of a file to upload.")]
+ public string? FilePath { get; set; }
+ }
+
+ public static async Task Main(string[] args)
+ {
+
+ var parserResult = Parser.Default.ParseArguments(args);
+ if (parserResult.Errors.Any())
+ {
+ Environment.Exit(1);
+ }
+ var option = parserResult.Value;
+
+ // Specify the region and other parameters.
+ var region = option.Region;
+ var bucket = option.Bucket;
+ var endpoint = option.Endpoint;
+ var key = option.Key;
+ var filePath = option.FilePath!;
+ long partSize = option.PartSize ?? 512 * 1024;
+
+ // Using the SDK's default configuration
+ // loading credentials values from the environment variables
+ var cfg = OSS.Configuration.LoadDefault();
+ cfg.CredentialsProvider = new OSS.Credentials.EnvironmentVariableCredentialsProvider();
+ cfg.Region = region;
+
+ if (endpoint != null)
+ {
+ cfg.Endpoint = endpoint;
+ }
+
+ using var client = new OSS.Client(cfg);
+
+ var initResult = await client.InitiateMultipartUploadAsync(new()
+ {
+ Bucket = bucket,
+ Key = key
+ });
+
+ // presign and upload
+ using var hc = new HttpClient();
+ using var file = File.OpenRead(filePath);
+ long fileSize = file.Length;
+ long partNumber = 1;
+
+ for (long offset = 0; offset < fileSize; offset += partSize)
+ {
+ var size = Math.Min(partSize, fileSize - offset);
+ var presignResult = client.Presign(new OSS.Models.UploadPartRequest
+ {
+ Bucket = bucket,
+ Key = key,
+ PartNumber = partNumber,
+ UploadId = initResult.UploadId,
+ Body = new OSS.IO.BoundedStream(file, offset, size)
+ });
+ var httpResult = await hc.PutAsync(
+ presignResult.Url,
+ new StreamContent(new OSS.IO.BoundedStream(file, offset, size))
+ );
+
+ if (!httpResult.IsSuccessStatusCode)
+ {
+ throw new Exception("upload part fail");
+ }
+ partNumber++;
+ }
+
+ // list parts
+ var uploadParts = new List();
+ var paginator = client.ListPartsPaginator(new ()
+ {
+ Bucket = bucket,
+ Key = key,
+ UploadId = initResult.UploadId,
+ });
+
+ await foreach (var page in paginator.IterPageAsync())
+ {
+ foreach (var part in page.Parts ?? [])
+ {
+ uploadParts.Add(new () { PartNumber = part.PartNumber, ETag = part.ETag });
+ }
+ }
+
+ // complete
+ uploadParts.Sort((left, right) => { return (left.PartNumber > right.PartNumber) ? 1 : -1; });
+ var cmResult = await client.CompleteMultipartUploadAsync(new()
+ {
+ Bucket = bucket,
+ Key = key,
+ UploadId = initResult.UploadId,
+ CompleteMultipartUpload = new()
+ {
+ Parts = uploadParts
+ }
+ });
+
+ Console.WriteLine("MultipartUpload done");
+ Console.WriteLine($"StatusCode: {cmResult.StatusCode}");
+ Console.WriteLine($"RequestId: {cmResult.RequestId}");
+ Console.WriteLine("Response Headers:");
+ cmResult.Headers.ToList().ForEach(x => Console.WriteLine(x.Key + " : " + x.Value));
+ }
+ }
+}
diff --git a/sample/alibabacloud-oss-csharp-sdk-v2-sample.sln b/sample/alibabacloud-oss-csharp-sdk-v2-sample.sln
index c798d7b..0d8d19e 100644
--- a/sample/alibabacloud-oss-csharp-sdk-v2-sample.sln
+++ b/sample/alibabacloud-oss-csharp-sdk-v2-sample.sln
@@ -80,6 +80,11 @@ EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DeleteBucket", "DeleteBucket\DeleteBucket.csproj", "{1CFCF2DD-C9F6-4903-A530-C7B813A6ED27}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AliyunCredentialsUsage", "AliyunCredentialsUsage\AliyunCredentialsUsage.csproj", "{02A88682-C702-461A-BE45-03FE071774F1}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MultipartUpload", "MultipartUpload\MultipartUpload.csproj", "{CEC023D4-F319-47E0-B6AA-A3B028A81FCB}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PresignMultipartUpload", "PresignMultipartUpload\PresignMultipartUpload.csproj", "{84BC7ED8-AF75-462D-8969-1ADCAD75387B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PostObject", "PostObject\PostObject.csproj", "{7816C6AA-0730-4CE7-B9AA-469BC85A136B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -243,6 +248,18 @@ Global
{02A88682-C702-461A-BE45-03FE071774F1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{02A88682-C702-461A-BE45-03FE071774F1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{02A88682-C702-461A-BE45-03FE071774F1}.Release|Any CPU.Build.0 = Release|Any CPU
+ {CEC023D4-F319-47E0-B6AA-A3B028A81FCB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CEC023D4-F319-47E0-B6AA-A3B028A81FCB}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CEC023D4-F319-47E0-B6AA-A3B028A81FCB}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CEC023D4-F319-47E0-B6AA-A3B028A81FCB}.Release|Any CPU.Build.0 = Release|Any CPU
+ {84BC7ED8-AF75-462D-8969-1ADCAD75387B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {84BC7ED8-AF75-462D-8969-1ADCAD75387B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {84BC7ED8-AF75-462D-8969-1ADCAD75387B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {84BC7ED8-AF75-462D-8969-1ADCAD75387B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {7816C6AA-0730-4CE7-B9AA-469BC85A136B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {7816C6AA-0730-4CE7-B9AA-469BC85A136B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {7816C6AA-0730-4CE7-B9AA-469BC85A136B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {7816C6AA-0730-4CE7-B9AA-469BC85A136B}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/src/AlibabaCloud.OSS.V2/Credentials/CredentialsProvideFunc.cs b/src/AlibabaCloud.OSS.V2/Credentials/CredentialsProvideFunc.cs
index 66c6387..88f1e0e 100644
--- a/src/AlibabaCloud.OSS.V2/Credentials/CredentialsProvideFunc.cs
+++ b/src/AlibabaCloud.OSS.V2/Credentials/CredentialsProvideFunc.cs
@@ -1,10 +1,13 @@
+using System;
+
namespace AlibabaCloud.OSS.V2.Credentials
{
///
- /// CredentialsProviderFunc provides a helper wrapping a function value
+ /// CredentialsProvideFunc provides a helper wrapping a function value
/// to satisfy the ICredentialsProvider interface.
///
+ [Obsolete("Please use CredentialsProviderFunc instead")]
public class CredentialsProvideFunc : ICredentialsProvider
{
public delegate Credentials GetCredentialsFunc();
diff --git a/src/AlibabaCloud.OSS.V2/Credentials/CredentialsProviderFunc.cs b/src/AlibabaCloud.OSS.V2/Credentials/CredentialsProviderFunc.cs
new file mode 100644
index 0000000..de923cb
--- /dev/null
+++ b/src/AlibabaCloud.OSS.V2/Credentials/CredentialsProviderFunc.cs
@@ -0,0 +1,25 @@
+
+namespace AlibabaCloud.OSS.V2.Credentials
+{
+ ///
+ /// CredentialsProviderFunc provides a helper wrapping a function value
+ /// to satisfy the ICredentialsProvider interface.
+ ///
+ public class CredentialsProviderFunc : ICredentialsProvider
+ {
+ public delegate Credentials GetCredentialsFunc();
+
+ private readonly GetCredentialsFunc _func;
+ ///
+ /// Creates an instance of
+ ///
+ /// a function that returns Credentials
+ public CredentialsProviderFunc(GetCredentialsFunc func) => _func = func;
+
+ ///
+ public Credentials GetCredentials()
+ {
+ return _func();
+ }
+ }
+}
diff --git a/src/AlibabaCloud.OSS.V2/Credentials/StaticCredentialsProvide.cs b/src/AlibabaCloud.OSS.V2/Credentials/StaticCredentialsProvide.cs
index 1bfdace..8a6c7d0 100644
--- a/src/AlibabaCloud.OSS.V2/Credentials/StaticCredentialsProvide.cs
+++ b/src/AlibabaCloud.OSS.V2/Credentials/StaticCredentialsProvide.cs
@@ -1,9 +1,12 @@
+using System;
+
namespace AlibabaCloud.OSS.V2.Credentials
{
///
/// Explicitly specify the AccessKey pair that you want to use to access OSS.
///
+ [Obsolete("Please use StaticCredentialsProvider instead")]
public class StaticCredentialsProvide : ICredentialsProvider
{
private readonly Credentials _credentials;
diff --git a/src/AlibabaCloud.OSS.V2/Credentials/StaticCredentialsProvider.cs b/src/AlibabaCloud.OSS.V2/Credentials/StaticCredentialsProvider.cs
new file mode 100644
index 0000000..844e78b
--- /dev/null
+++ b/src/AlibabaCloud.OSS.V2/Credentials/StaticCredentialsProvider.cs
@@ -0,0 +1,37 @@
+
+namespace AlibabaCloud.OSS.V2.Credentials
+{
+ ///
+ /// Explicitly specify the AccessKey pair that you want to use to access OSS.
+ ///
+ public class StaticCredentialsProvider : ICredentialsProvider
+ {
+ private readonly Credentials _credentials;
+ ///
+ /// Creates an instance of
+ ///
+ ///
+ ///
+ public StaticCredentialsProvider(string accessKeyId, string accessKeySecret)
+ {
+ _credentials = new(accessKeyId, accessKeySecret);
+ }
+
+ ///
+ /// Creates an instance of
+ ///
+ ///
+ ///
+ ///
+ public StaticCredentialsProvider(string accessKeyId, string accessKeySecret, string securityToken)
+ {
+ _credentials = new(accessKeyId, accessKeySecret, securityToken);
+ }
+
+ ///
+ public Credentials GetCredentials()
+ {
+ return _credentials;
+ }
+ }
+}
diff --git a/src/AlibabaCloud.OSS.V2/IO/BoundedStream.cs b/src/AlibabaCloud.OSS.V2/IO/BoundedStream.cs
new file mode 100644
index 0000000..fe71261
--- /dev/null
+++ b/src/AlibabaCloud.OSS.V2/IO/BoundedStream.cs
@@ -0,0 +1,200 @@
+using System;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace AlibabaCloud.OSS.V2.IO
+{
+ ///
+ /// Represents read-only view over the portion of underlying stream.
+ ///
+ public sealed class BoundedStream : Stream
+ {
+ private long _length;
+ private long _offset;
+
+ ///
+ /// Creates a new BoundedStream that wraps the given stream.
+ ///
+ /// The wrapped input stream.
+ public BoundedStream(Stream stream)
+ {
+ this._length = stream.Length;
+ BaseStream = stream;
+ }
+
+ ///
+ /// Creates a new BoundedStream that wraps the given stream and limits it to a certain size.
+ ///
+ /// The wrapped input stream.
+ /// The offset in the underlying stream.
+ /// Total number of bytes to allow to be read from the stream.
+ public BoundedStream(Stream stream, long offset, long length)
+ {
+ BaseStream = stream;
+ Adjust(offset, length);
+ }
+
+ ///
+ /// Gets underlying stream.
+ ///
+ public Stream BaseStream { get; private set; }
+
+ ///
+ /// Adjust the stream bounds.
+ ///
+ ///
+ /// This method modifies property of the underlying stream.
+ ///
+ /// The offset in the underlying stream.
+ /// Total number of bytes to allow to be read from the stream.
+ /// is larger than the remaining length of the underlying stream; or if greater than the length of the underlying stream.
+ public void Adjust(long offset, long length)
+ {
+ ThrowIfGreaterThan((ulong)offset, (ulong)BaseStream.Length, nameof(offset));
+ ThrowIfGreaterThan((ulong)length, (ulong)(BaseStream.Length - offset), nameof(length));
+ this._length = length;
+ this._offset = offset;
+ BaseStream.Position = offset;
+ }
+
+ ///
+ /// Gets a value indicating whether the current stream supports reading.
+ ///
+ /// if the stream supports reading; otherwise, .
+ public override bool CanRead => BaseStream.CanRead;
+
+ ///
+ /// Gets a value indicating whether the current stream supports seeking.
+ ///
+ /// if the stream supports seeking; otherwise, .
+ public override bool CanSeek => BaseStream.CanSeek;
+
+ ///
+ /// Gets a value indicating whether the current stream supports writing.
+ ///
+ /// Always .
+ public override bool CanWrite => false;
+
+ ///
+ public override long Length => _length;
+
+ ///
+ public override long Position
+ {
+ get => BaseStream.Position - _offset;
+ set
+ {
+ ThrowIfGreaterThan(value, _length, nameof(value));
+ BaseStream.Position = _offset + value;
+ }
+ }
+
+ private long RemainingBytes => _length - Position;
+
+ ///
+ public override void Flush() => BaseStream.Flush();
+
+ ///
+ public override Task FlushAsync(CancellationToken token = default) => BaseStream.FlushAsync(token);
+
+ ///
+ public override bool CanTimeout => BaseStream.CanTimeout;
+
+ ///
+ public override int ReadByte()
+ => Position < _length ? BaseStream.ReadByte() : -1;
+
+ ///
+ public override void WriteByte(byte value) => throw new NotSupportedException();
+
+ ///
+ public override int Read(byte[] buffer, int offset, int count)
+ {
+ return BaseStream.Read(buffer, offset, (int)Math.Min(count, RemainingBytes));
+ }
+
+ ///
+ public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
+ {
+ count = (int)Math.Min(count, RemainingBytes);
+ return BaseStream.BeginRead(buffer, offset, count, callback, state);
+ }
+
+ ///
+ public override int EndRead(IAsyncResult asyncResult) => BaseStream.EndRead(asyncResult);
+
+ ///
+ public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken token = default)
+ => BaseStream.ReadAsync(buffer, offset, (int)Math.Min(count, RemainingBytes), token);
+
+ ///
+ public override long Seek(long offset, SeekOrigin origin)
+ {
+ var newPosition = origin switch
+ {
+ SeekOrigin.Begin => offset,
+ SeekOrigin.Current => Position + offset,
+ SeekOrigin.End => _length + offset,
+ _ => throw new ArgumentOutOfRangeException(nameof(origin))
+ };
+
+ if (newPosition < 0L)
+ throw new IOException();
+
+ ThrowIfGreaterThan(newPosition, _length, nameof(offset));
+
+ Position = newPosition;
+ return newPosition;
+ }
+
+ ///
+ public override void SetLength(long value)
+ {
+ ThrowIfGreaterThan((ulong)value, (ulong)(BaseStream.Length - BaseStream.Position), nameof(value));
+ _length = value;
+ }
+
+ ///
+ public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException();
+
+ ///
+ public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken token = default) => Task.FromException(new NotSupportedException());
+
+ ///
+ public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state)
+ => throw new NotSupportedException();
+
+ ///
+ public override void EndWrite(IAsyncResult asyncResult) => throw new InvalidOperationException();
+
+ ///
+ public override int ReadTimeout
+ {
+ get => BaseStream.ReadTimeout;
+ set => BaseStream.ReadTimeout = value;
+ }
+
+ ///
+ public override int WriteTimeout
+ {
+ get => BaseStream.WriteTimeout;
+ set => BaseStream.WriteTimeout = value;
+ }
+
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ base.Dispose(disposing);
+ BaseStream.Dispose();
+ }
+
+ private static void ThrowIfGreaterThan(T value, T other, string? paramName = null) where T : IComparable
+ {
+ if (value.CompareTo(other) > 0)
+ {
+ throw new ArgumentOutOfRangeException(paramName, value, $"{paramName} ('{value}')must be less than or equal to '{other}'");
+ }
+ }
+ }
+}
diff --git a/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientMiscTest.cs b/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientMiscTest.cs
index da4a7fa..6d13a0d 100644
--- a/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientMiscTest.cs
+++ b/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientMiscTest.cs
@@ -1,4 +1,5 @@
using System.Text;
+using AlibabaCloud.OSS.V2.IO;
using AlibabaCloud.OSS.V2.Models;
namespace AlibabaCloud.OSS.V2.IntegrationTests;
@@ -516,7 +517,7 @@ public async Task TestPutGetObjectByFilePath()
public async Task TestInsecureSkipVerifyTrue()
{
var cfg = Configuration.LoadDefault();
- cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide(Utils.AccessKeyId, Utils.AccessKeySecret);
+ cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider(Utils.AccessKeyId, Utils.AccessKeySecret);
cfg.Region = Utils.Region;
cfg.InsecureSkipVerify = true;
@@ -900,7 +901,7 @@ public async Task TestPutGetObjectByFilePathWithProgress()
// disable crc
var cfg = Configuration.LoadDefault();
- cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide(Utils.AccessKeyId, Utils.AccessKeySecret);
+ cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider(Utils.AccessKeyId, Utils.AccessKeySecret);
cfg.Region = Utils.Region;
cfg.Endpoint = Utils.Endpoint;
cfg.DisableDownloadCrc64Check = true;
@@ -1005,4 +1006,106 @@ public async Task TestQueryWithSpecialChar()
var got1 = await reader1.ReadToEndAsync();
Assert.Equal(content, got1);
}
+
+ [Fact]
+ public async Task TestUploadPartFromFile()
+ {
+ var client = Utils.GetDefaultClient();
+
+ //default
+ var bucketName = Utils.RandomBucketName(BucketNamePrefix);
+
+ var result = await client.PutBucketAsync(new()
+ {
+ Bucket = bucketName
+ });
+
+ Assert.NotNull(result);
+ Assert.Equal(200, result.StatusCode);
+ Assert.NotNull(result.RequestId);
+
+ var partSize = 120 * 1024;
+ var filepath = Utils.RandomFilePath(RootPath);
+ var saveFilepath = Utils.RandomFilePath(RootPath);
+ Utils.PrepareSampleFile(filepath, 512);
+ Assert.True(File.Exists(filepath));
+
+ // init
+ var objectName = Utils.RandomObjectName();
+ var initResult = await client.InitiateMultipartUploadAsync(new()
+ {
+ Bucket = bucketName,
+ Key = objectName
+ }
+ );
+ Assert.NotNull(initResult);
+ Assert.Equal(200, initResult.StatusCode);
+ Assert.NotNull(initResult.RequestId);
+ Assert.Equal("url", initResult.EncodingType);
+
+ // upload
+ using var file = File.OpenRead(filepath);
+ var fileSize = file.Length;
+ long partNumber = 1;
+ for (int offset = 0; offset < fileSize; offset += partSize)
+ {
+ var size = (long)Math.Min(partSize, fileSize - offset);
+ var upResult = await client.UploadPartAsync(new()
+ {
+ Bucket = bucketName,
+ Key = objectName,
+ PartNumber = partNumber,
+ UploadId = initResult.UploadId,
+ Body = new BoundedStream(file, (long)offset, size)
+ });
+
+ partNumber++;
+
+ Assert.NotNull(upResult);
+ Assert.Equal(200, upResult.StatusCode);
+ Assert.NotNull(upResult.RequestId);
+ }
+
+ // complete
+ var cmResult = await client.CompleteMultipartUploadAsync(new()
+ {
+ Bucket = bucketName,
+ Key = objectName,
+ UploadId = initResult.UploadId,
+ CompleteAll = "yes"
+ });
+ Assert.NotNull(cmResult);
+ Assert.Equal(200, cmResult.StatusCode);
+ Assert.NotNull(cmResult.RequestId);
+ Assert.Equal("url", cmResult.EncodingType);
+
+ // get object
+ var getObjectResult = await client.GetObjectAsync(
+ new()
+ {
+ Bucket = bucketName,
+ Key = objectName
+ }
+ );
+ Assert.NotNull(getObjectResult);
+ Assert.Equal(200, getObjectResult.StatusCode);
+ Assert.Equal((long)fileSize, getObjectResult.ContentLength);
+ Assert.Equal("Multipart", getObjectResult.ObjectType);
+ Assert.Null(getObjectResult.TaggingCount);
+ Assert.Null(getObjectResult.ContentMd5);
+ Assert.NotNull(getObjectResult.StorageClass);
+ Assert.NotNull(getObjectResult.Metadata);
+ Assert.Empty(getObjectResult.Metadata);
+ Assert.NotNull(getObjectResult.Body);
+ Assert.False(getObjectResult.Body.CanSeek);
+
+ using var saveStream = File.OpenWrite(saveFilepath);
+ await getObjectResult.Body.CopyToAsync(saveStream);
+ saveStream.Close();
+
+ var srcMd5 = Utils.ComputeContentMd5(filepath);
+ var dstMd5 = Utils.ComputeContentMd5(saveFilepath);
+ Assert.NotEmpty(srcMd5);
+ Assert.Equal(srcMd5, dstMd5);
+ }
}
diff --git a/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientObjectMultipartTest.cs b/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientObjectMultipartTest.cs
index 4c295ff..0847c08 100644
--- a/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientObjectMultipartTest.cs
+++ b/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientObjectMultipartTest.cs
@@ -781,7 +781,7 @@ await invClient.ListMultipartUploadsAsync(
public async Task TestUploadPartWithCrcCheckDisable()
{
var cfg = Configuration.LoadDefault();
- cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide(Utils.AccessKeyId, Utils.AccessKeySecret);
+ cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider(Utils.AccessKeyId, Utils.AccessKeySecret);
cfg.Region = Utils.Region;
cfg.Endpoint = Utils.Endpoint;
cfg.DisableUploadCrc64Check = true;
diff --git a/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientObjectTest.cs b/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientObjectTest.cs
index f3df47a..751bbd8 100644
--- a/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientObjectTest.cs
+++ b/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientObjectTest.cs
@@ -1526,7 +1526,7 @@ public async Task TestAppendObjectWithCrcCheckEnable()
public async Task TestAppendObjectWithCrcCheckDisable()
{
var cfg = Configuration.LoadDefault();
- cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide(Utils.AccessKeyId, Utils.AccessKeySecret);
+ cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider(Utils.AccessKeyId, Utils.AccessKeySecret);
cfg.Region = Utils.Region;
cfg.Endpoint = Utils.Endpoint;
cfg.DisableUploadCrc64Check = true;
@@ -1572,7 +1572,7 @@ public async Task TestAppendObjectWithCrcCheckDisable()
public async Task TestPutObjectWithCrcCheckDisable()
{
var cfg = Configuration.LoadDefault();
- cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide(Utils.AccessKeyId, Utils.AccessKeySecret);
+ cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider(Utils.AccessKeyId, Utils.AccessKeySecret);
cfg.Region = Utils.Region;
cfg.Endpoint = Utils.Endpoint;
cfg.DisableUploadCrc64Check = true;
@@ -1836,7 +1836,7 @@ public async Task TestGetObjectWithCrcCheckEnable()
public async Task TestGetObjectWithCrcCheckDisable()
{
var cfg = Configuration.LoadDefault();
- cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide(Utils.AccessKeyId, Utils.AccessKeySecret);
+ cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider(Utils.AccessKeyId, Utils.AccessKeySecret);
cfg.Region = Utils.Region;
cfg.Endpoint = Utils.Endpoint;
cfg.DisableDownloadCrc64Check = true;
diff --git a/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientPresignerTest.cs b/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientPresignerTest.cs
index 3ed547f..3fa102a 100644
--- a/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientPresignerTest.cs
+++ b/test/AlibabaCloud.OSS.V2.IntegrationTests/ClientPresignerTest.cs
@@ -196,7 +196,7 @@ out var expires
public async Task TestPresignGetAndPutObjectV1()
{
var cfg = Configuration.LoadDefault();
- cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide(Utils.AccessKeyId, Utils.AccessKeySecret);
+ cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider(Utils.AccessKeyId, Utils.AccessKeySecret);
cfg.Region = "cn-shenzhen";
cfg.SignatureVersion = "v1";
@@ -423,7 +423,7 @@ public async Task TestPresignAbortMultipartUpload()
public void TestPresignFail()
{
var cfg = Configuration.LoadDefault();
- cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide("", "");
+ cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider("", "");
cfg.Region = Utils.Region;
cfg.Endpoint = Utils.Endpoint;
@@ -546,7 +546,7 @@ public void TestPresignFail()
public void TestPresignWithExpirationTimeV4()
{
var cfg = Configuration.LoadDefault();
- cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide("ak", "sk");
+ cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider("ak", "sk");
cfg.Region = "cn-shenzhen";
using var client = new Client(cfg);
@@ -671,7 +671,7 @@ public void TestPresignWithExpirationTimeV4()
public void TestPresignWithExpirationTimeV1()
{
var cfg = Configuration.LoadDefault();
- cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide("ak", "sk");
+ cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider("ak", "sk");
cfg.Region = "cn-shenzhen";
cfg.SignatureVersion = "v1";
@@ -800,7 +800,7 @@ public void TestPresignWithPresignExpirationException()
// v1 supports > 7 days Expiration
var cfg = Configuration.LoadDefault();
- cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide("ak", "sk");
+ cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider("ak", "sk");
cfg.Region = "cn-shenzhen";
cfg.SignatureVersion = "v1";
@@ -831,7 +831,7 @@ public void TestPresignWithPresignExpirationException()
// v4 does not support > 7 days Expiration
cfg = Configuration.LoadDefault();
- cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide("ak", "sk");
+ cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider("ak", "sk");
cfg.Region = "cn-shenzhen";
cfg.SignatureVersion = "v4";
diff --git a/test/AlibabaCloud.OSS.V2.IntegrationTests/Utils.cs b/test/AlibabaCloud.OSS.V2.IntegrationTests/Utils.cs
index 590b27f..3cc70f8 100644
--- a/test/AlibabaCloud.OSS.V2.IntegrationTests/Utils.cs
+++ b/test/AlibabaCloud.OSS.V2.IntegrationTests/Utils.cs
@@ -54,7 +54,7 @@ public static Client GetDefaultClient()
if (_client != null) return _client;
var cfg = Configuration.LoadDefault();
- cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide(AccessKeyId, AccessKeySecret);
+ cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider(AccessKeyId, AccessKeySecret);
cfg.Region = Region;
cfg.Endpoint = Endpoint;
@@ -66,7 +66,7 @@ public static Client GetDefaultClient()
public static Client GetClient(string region, string endpoint)
{
var cfg = Configuration.LoadDefault();
- cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide(AccessKeyId, AccessKeySecret);
+ cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider(AccessKeyId, AccessKeySecret);
cfg.Region = region;
cfg.Endpoint = endpoint;
@@ -78,7 +78,7 @@ public static Client GetInvalidAkClient()
if (_invalidClient != null) return _invalidClient;
var cfg = Configuration.LoadDefault();
- cfg.CredentialsProvider = new Credentials.StaticCredentialsProvide("invalid-ak", "invalid-sk");
+ cfg.CredentialsProvider = new Credentials.StaticCredentialsProvider("invalid-ak", "invalid-sk");
cfg.Region = Region;
cfg.Endpoint = Endpoint;
diff --git a/test/AlibabaCloud.OSS.V2.UnitTests/Credentials/CredentialsTest.cs b/test/AlibabaCloud.OSS.V2.UnitTests/Credentials/CredentialsTest.cs
index 250d3e3..dd111e0 100644
--- a/test/AlibabaCloud.OSS.V2.UnitTests/Credentials/CredentialsTest.cs
+++ b/test/AlibabaCloud.OSS.V2.UnitTests/Credentials/CredentialsTest.cs
@@ -58,7 +58,7 @@ public void TestCredentials()
[Fact]
public void TestStaticCredentialsProvide()
{
- var provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk");
+ var provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk");
var cred = provider.GetCredentials();
Assert.Equal("ak", cred.AccessKeyId);
Assert.Equal("sk", cred.AccessKeySecret);
@@ -67,7 +67,7 @@ public void TestStaticCredentialsProvide()
Assert.Null(cred.Expiration);
Assert.False(cred.IsExpired);
- provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk", "token");
+ provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk", "token");
cred = provider.GetCredentials();
Assert.Equal("ak", cred.AccessKeyId);
Assert.Equal("sk", cred.AccessKeySecret);
@@ -94,7 +94,7 @@ public void TestAnonymousCredentialsProvider()
public void TestCredentialsProvideFunc()
{
- var provider = new V2.Credentials.CredentialsProvideFunc(() => new V2.Credentials.Credentials("ak", "sk"));
+ var provider = new V2.Credentials.CredentialsProviderFunc(() => new V2.Credentials.Credentials("ak", "sk"));
var cred = provider.GetCredentials();
Assert.Equal("ak", cred.AccessKeyId);
Assert.Equal("sk", cred.AccessKeySecret);
@@ -103,7 +103,7 @@ public void TestCredentialsProvideFunc()
Assert.Null(cred.Expiration);
Assert.False(cred.IsExpired);
- provider = new V2.Credentials.CredentialsProvideFunc(() => new V2.Credentials.Credentials("ak", "sk", "token"));
+ provider = new V2.Credentials.CredentialsProviderFunc(() => new V2.Credentials.Credentials("ak", "sk", "token"));
cred = provider.GetCredentials();
Assert.Equal("ak", cred.AccessKeyId);
Assert.Equal("sk", cred.AccessKeySecret);
diff --git a/test/AlibabaCloud.OSS.V2.UnitTests/IO/BoundedStreamTest.cs b/test/AlibabaCloud.OSS.V2.UnitTests/IO/BoundedStreamTest.cs
new file mode 100644
index 0000000..ee2fc98
--- /dev/null
+++ b/test/AlibabaCloud.OSS.V2.UnitTests/IO/BoundedStreamTest.cs
@@ -0,0 +1,200 @@
+using System.Text;
+using AlibabaCloud.OSS.V2.IO;
+
+namespace AlibabaCloud.OSS.V2.UnitTests.Models;
+
+public class BoundedStreamTest
+{
+ [Theory]
+ [InlineData(0, 4, "This")]
+ [InlineData(5, 2, "is")]
+ [InlineData(10, 4, "test")]
+ public static void AdjustSetsBoundedOfStream(int offset, int length, string expected)
+ {
+ using var ms = new MemoryStream(Encoding.UTF8.GetBytes("This is a test"));
+ using var bounded = new BoundedStream(ms);
+ bounded.Adjust(offset, length);
+ using StreamReader reader = new(bounded);
+ Assert.Equal(expected, reader.ReadToEnd());
+ }
+
+ [Theory]
+ [InlineData(0, 4, "This")]
+ [InlineData(5, 2, "is")]
+ [InlineData(10, 4, "test")]
+ public static void CreatesBoundedOfStream(int offset, int length, string expected)
+ {
+ using var ms = new MemoryStream(Encoding.UTF8.GetBytes("This is a test"));
+ using var bounded = new BoundedStream(ms, offset, length);
+ using StreamReader reader = new(bounded);
+ Assert.Equal(expected, reader.ReadToEnd());
+ }
+
+ [Fact]
+ public static void ReadByteSequentially()
+ {
+ using var ms = new MemoryStream([1, 3, 5, 8, 12]);
+ using var bounded = new BoundedStream(ms);
+ Assert.Same(ms, bounded.BaseStream);
+ Assert.Equal(0, bounded.Position);
+ bounded.Adjust(0, 2);
+ Assert.Equal(1, bounded.ReadByte());
+ Assert.Equal(1, bounded.Position);
+
+ Assert.Equal(3, bounded.ReadByte());
+ Assert.Equal(2, bounded.Position);
+
+ Assert.Equal(-1, bounded.ReadByte());
+ Assert.Equal(2, bounded.Position);
+ }
+
+ [Fact]
+ public static void CreatesAndReadByteSequentially()
+ {
+ using var ms = new MemoryStream([1, 3, 5, 8, 12]);
+ using var bounded = new BoundedStream(ms, 0, 2);
+ Assert.Same(ms, bounded.BaseStream);
+ Assert.Equal(0, bounded.Position);
+
+ Assert.Equal(1, bounded.ReadByte());
+ Assert.Equal(1, bounded.Position);
+
+ Assert.Equal(3, bounded.ReadByte());
+ Assert.Equal(2, bounded.Position);
+
+ Assert.Equal(-1, bounded.ReadByte());
+ Assert.Equal(2, bounded.Position);
+ }
+
+ [Fact]
+ public static void SetPosition()
+ {
+ using var ms = new MemoryStream([1, 3, 5, 8, 12]);
+ using var bounded = new BoundedStream(ms);
+ bounded.Adjust(1, 3);
+ bounded.Position = 1;
+ Assert.Equal(5, bounded.ReadByte());
+ Assert.Equal(2, bounded.Position);
+ bounded.Position = 0;
+ Assert.Equal(3, bounded.ReadByte());
+ Assert.Equal(1, bounded.Position);
+ }
+
+ [Fact]
+ public static void CreatesAndSetPosition()
+ {
+ using var ms = new MemoryStream([1, 3, 5, 8, 12]);
+ using var bounded = new BoundedStream(ms, 1, 3);
+ bounded.Position = 1;
+ Assert.Equal(5, bounded.ReadByte());
+ Assert.Equal(2, bounded.Position);
+ bounded.Position = 0;
+ Assert.Equal(3, bounded.ReadByte());
+ Assert.Equal(1, bounded.Position);
+ }
+
+ [Fact]
+ public static void ReadRange()
+ {
+ using var ms = new MemoryStream([1, 3, 5, 8, 12]);
+ using var bounded = new BoundedStream(ms);
+ bounded.Adjust(1L, 2L);
+ var buffer = new byte[4];
+ Assert.Equal(2, bounded.Read(buffer, 0, buffer.Length));
+ Assert.Equal(3, buffer[0]);
+ Assert.Equal(5, buffer[1]);
+ Assert.Equal(0, buffer[2]);
+ Assert.Equal(0, buffer[3]);
+ //read from the end of the stream
+ Assert.Equal(-1, bounded.ReadByte());
+ }
+
+ [Fact]
+ public static void CreatesAndReadRange()
+ {
+ using var ms = new MemoryStream([1, 3, 5, 8, 12]);
+ using var bounded = new BoundedStream(ms, 1L, 2L);
+ var buffer = new byte[4];
+ Assert.Equal(2, bounded.Read(buffer, 0, buffer.Length));
+ Assert.Equal(3, buffer[0]);
+ Assert.Equal(5, buffer[1]);
+ Assert.Equal(0, buffer[2]);
+ Assert.Equal(0, buffer[3]);
+ //read from the end of the stream
+ Assert.Equal(-1, bounded.ReadByte());
+ }
+
+ [Fact]
+ public static async Task ReadRangeAsync()
+ {
+ using var ms = new MemoryStream([1, 3, 5, 8, 12]);
+ using var bounded = new BoundedStream(ms);
+ bounded.Adjust(1L, 2L);
+ var buffer = new byte[4];
+ Assert.Equal(2, await bounded.ReadAsync(buffer, 0, buffer.Length));
+ Assert.Equal(3, buffer[0]);
+ Assert.Equal(5, buffer[1]);
+ Assert.Equal(0, buffer[2]);
+ Assert.Equal(0, buffer[3]);
+ //read from the end of the stream
+ Assert.Equal(-1, bounded.ReadByte());
+ }
+
+ [Fact]
+ public static async Task CreatesAndReadRangeAsync()
+ {
+ using var ms = new MemoryStream([1, 3, 5, 8, 12]);
+ using var bounded = new BoundedStream(ms, 1L, 2L);
+ var buffer = new byte[4];
+ Assert.Equal(2, await bounded.ReadAsync(buffer, 0, buffer.Length));
+ Assert.Equal(3, buffer[0]);
+ Assert.Equal(5, buffer[1]);
+ Assert.Equal(0, buffer[2]);
+ Assert.Equal(0, buffer[3]);
+ //read from the end of the stream
+ Assert.Equal(-1, bounded.ReadByte());
+ }
+
+ [Fact]
+ public static void ReadApm()
+ {
+ using var ms = new MemoryStream([1, 3, 5, 8, 12]);
+ using var bounded = new BoundedStream(ms);
+ bounded.Adjust(1L, 2L);
+ var buffer = new byte[4];
+ var ar = bounded.BeginRead(buffer, 0, 2, null, null);
+ Assert.Equal(2, bounded.EndRead(ar));
+ Assert.Equal(3, buffer[0]);
+ Assert.Equal(5, buffer[1]);
+ Assert.Equal(0, buffer[2]);
+ }
+
+ [Fact]
+ public static void CreatesAndReadApm()
+ {
+ using var ms = new MemoryStream([1, 3, 5, 8, 12]);
+ using var bounded = new BoundedStream(ms, 1L, 2L);
+ var buffer = new byte[4];
+ var ar = bounded.BeginRead(buffer, 0, 2, null, null);
+ Assert.Equal(2, bounded.EndRead(ar));
+ Assert.Equal(3, buffer[0]);
+ Assert.Equal(5, buffer[1]);
+ Assert.Equal(0, buffer[2]);
+ }
+
+ [Fact]
+ public static async Task ExceptionCheck()
+ {
+ using var ms = new MemoryStream([1, 3, 5, 8, 12]);
+ using var bounded = new BoundedStream(ms);
+ Assert.True(bounded.CanRead);
+ Assert.True(bounded.CanSeek);
+ Assert.False(bounded.CanWrite);
+ Assert.Equal(ms.CanTimeout, bounded.CanTimeout);
+ Assert.Throws(() => bounded.WriteByte(2));
+ Assert.Throws(() => bounded.Write(new byte[3], 0, 3));
+ Assert.Throws(() => bounded.BeginWrite(new byte[2], 0, 2, null, null));
+ Assert.Throws(() => bounded.EndWrite(Task.CompletedTask));
+ await Assert.ThrowsAsync(() => bounded.WriteAsync(new byte[3], 0, 3));
+ }
+}
diff --git a/test/AlibabaCloud.OSS.V2.UnitTests/Internal/ClientImplTest.cs b/test/AlibabaCloud.OSS.V2.UnitTests/Internal/ClientImplTest.cs
index 46dda3b..5c5963d 100644
--- a/test/AlibabaCloud.OSS.V2.UnitTests/Internal/ClientImplTest.cs
+++ b/test/AlibabaCloud.OSS.V2.UnitTests/Internal/ClientImplTest.cs
@@ -854,7 +854,7 @@ public async Task TestSignerV4()
var config = new Configuration()
{
Region = "cn-hangzhou",
- CredentialsProvider = new StaticCredentialsProvide("ak", "sk"),
+ CredentialsProvider = new StaticCredentialsProvider("ak", "sk"),
HttpTransport = new HttpTransport(mockHandler),
};
var client = new ClientImpl(config);
@@ -892,7 +892,7 @@ public async Task TestSignerV1()
var config = new Configuration()
{
Region = "cn-hangzhou",
- CredentialsProvider = new StaticCredentialsProvide("ak", "sk"),
+ CredentialsProvider = new StaticCredentialsProvider("ak", "sk"),
HttpTransport = new HttpTransport(mockHandler),
SignatureVersion = "v1"
};
@@ -1725,7 +1725,7 @@ public void TestPresignInnerV4()
var config = new Configuration()
{
Region = "cn-hangzhou",
- CredentialsProvider = new StaticCredentialsProvide("ak", "sk"),
+ CredentialsProvider = new StaticCredentialsProvider("ak", "sk"),
HttpTransport = new HttpTransport(mockHandler),
};
var client = new ClientImpl(config);
@@ -1795,7 +1795,7 @@ public void TestPresignInnerV4()
config = new Configuration()
{
Region = "cn-hangzhou",
- CredentialsProvider = new StaticCredentialsProvide("ak", "sk"),
+ CredentialsProvider = new StaticCredentialsProvider("ak", "sk"),
HttpTransport = new HttpTransport(mockHandler),
AdditionalHeaders = ["Abc"]
};
@@ -1842,7 +1842,7 @@ public void TestPresignInnerV4()
config = new Configuration()
{
Region = "cn-hangzhou",
- CredentialsProvider = new StaticCredentialsProvide("ak", "sk", "token"),
+ CredentialsProvider = new StaticCredentialsProvider("ak", "sk", "token"),
HttpTransport = new HttpTransport(mockHandler),
};
client = new ClientImpl(config);
@@ -1882,7 +1882,7 @@ public void TestPresignInnerV4DefaultExpiration()
var config = new Configuration()
{
Region = "cn-hangzhou",
- CredentialsProvider = new StaticCredentialsProvide("ak", "sk"),
+ CredentialsProvider = new StaticCredentialsProvider("ak", "sk"),
HttpTransport = new HttpTransport(mockHandler),
};
var client = new ClientImpl(config);
@@ -1919,7 +1919,7 @@ public void TestPresignInnerV1()
var config = new Configuration()
{
Region = "cn-hangzhou",
- CredentialsProvider = new StaticCredentialsProvide("ak", "sk"),
+ CredentialsProvider = new StaticCredentialsProvider("ak", "sk"),
HttpTransport = new HttpTransport(mockHandler),
SignatureVersion = "v1"
};
@@ -1985,7 +1985,7 @@ public void TestPresignInnerV1()
config = new Configuration()
{
Region = "cn-hangzhou",
- CredentialsProvider = new StaticCredentialsProvide("ak", "sk"),
+ CredentialsProvider = new StaticCredentialsProvider("ak", "sk"),
HttpTransport = new HttpTransport(mockHandler),
SignatureVersion = "v1",
AdditionalHeaders = ["Abc"]
@@ -2029,7 +2029,7 @@ public void TestPresignInnerV1()
config = new Configuration()
{
Region = "cn-hangzhou",
- CredentialsProvider = new StaticCredentialsProvide("ak", "sk", "token"),
+ CredentialsProvider = new StaticCredentialsProvider("ak", "sk", "token"),
HttpTransport = new HttpTransport(mockHandler),
SignatureVersion = "v1"
};
@@ -2068,7 +2068,7 @@ public void TestPresignInnerV1DefaultExpiration()
var config = new Configuration()
{
Region = "cn-hangzhou",
- CredentialsProvider = new StaticCredentialsProvide("ak", "sk"),
+ CredentialsProvider = new StaticCredentialsProvider("ak", "sk"),
HttpTransport = new HttpTransport(mockHandler),
SignatureVersion = "v1"
};
@@ -2106,7 +2106,7 @@ public void TestPresignMisc()
var config = new Configuration()
{
Region = "cn-hangzhou",
- CredentialsProvider = new StaticCredentialsProvide("ak", "sk"),
+ CredentialsProvider = new StaticCredentialsProvider("ak", "sk"),
HttpTransport = new HttpTransport(mockHandler),
};
var client = new ClientImpl(config);
@@ -2164,7 +2164,7 @@ public void TestPresignMisc()
config = new Configuration()
{
Region = "cn-hangzhou",
- CredentialsProvider = new StaticCredentialsProvide("", ""),
+ CredentialsProvider = new StaticCredentialsProvider("", ""),
HttpTransport = new HttpTransport(mockHandler),
};
client = new ClientImpl(config);
@@ -2191,7 +2191,7 @@ public void TestPresignMisc()
config = new Configuration()
{
Region = "cn-hangzhou",
- CredentialsProvider = new StaticCredentialsProvide("ak", "sk"),
+ CredentialsProvider = new StaticCredentialsProvider("ak", "sk"),
HttpTransport = new HttpTransport(mockHandler),
};
client = new ClientImpl(config);
@@ -2226,7 +2226,7 @@ public void TestDispose()
var config = new Configuration()
{
Region = "cn-hangzhou",
- CredentialsProvider = new StaticCredentialsProvide("ak", "sk"),
+ CredentialsProvider = new StaticCredentialsProvider("ak", "sk"),
HttpTransport = new HttpTransport(mockHandler),
};
using var client = new ClientImpl(config);
diff --git a/test/AlibabaCloud.OSS.V2.UnitTests/Signer/SignerV1Test.cs b/test/AlibabaCloud.OSS.V2.UnitTests/Signer/SignerV1Test.cs
index 329be75..ecdea79 100644
--- a/test/AlibabaCloud.OSS.V2.UnitTests/Signer/SignerV1Test.cs
+++ b/test/AlibabaCloud.OSS.V2.UnitTests/Signer/SignerV1Test.cs
@@ -7,7 +7,7 @@ public class SignerV1Test
[Fact]
public void TestAuthHeader()
{
- var provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk");
+ var provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk");
var cred = provider.GetCredentials();
//case 1
@@ -113,7 +113,7 @@ public void TestAuthHeader()
[Fact]
public void TestAuthHeaderToken()
{
- var provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk", "token");
+ var provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk", "token");
var cred = provider.GetCredentials();
//case 1
@@ -148,7 +148,7 @@ public void TestAuthHeaderToken()
[Fact]
public void TestAuthQuery()
{
- var provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk");
+ var provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk");
var cred = provider.GetCredentials();
//case 1
@@ -183,7 +183,7 @@ public void TestAuthQuery()
[Fact]
public void TestAuthQueryToken()
{
- var provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk", "token");
+ var provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk", "token");
var cred = provider.GetCredentials();
//case 1
diff --git a/test/AlibabaCloud.OSS.V2.UnitTests/Signer/SignerV4Test.cs b/test/AlibabaCloud.OSS.V2.UnitTests/Signer/SignerV4Test.cs
index 542e2e0..767798b 100644
--- a/test/AlibabaCloud.OSS.V2.UnitTests/Signer/SignerV4Test.cs
+++ b/test/AlibabaCloud.OSS.V2.UnitTests/Signer/SignerV4Test.cs
@@ -7,7 +7,7 @@ public class SignerV4Test
[Fact]
public void TestAuthHeader()
{
- var provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk");
+ var provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk");
var cred = provider.GetCredentials();
//case 1
@@ -64,7 +64,7 @@ public void TestAuthHeader()
[Fact]
public void TestAuthHeaderToken()
{
- var provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk", "token");
+ var provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk", "token");
var cred = provider.GetCredentials();
//case 1
@@ -121,7 +121,7 @@ public void TestAuthHeaderToken()
[Fact]
public void TestAuthHeaderWithAdditionalHeaders()
{
- var provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk");
+ var provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk");
var cred = provider.GetCredentials();
//case 1
@@ -210,7 +210,7 @@ public void TestAuthHeaderWithAdditionalHeaders()
[Fact]
public void TestAuthQuery()
{
- var provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk");
+ var provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk");
var cred = provider.GetCredentials();
//case 1
@@ -272,7 +272,7 @@ public void TestAuthQuery()
[Fact]
public void TestAuthQueryToken()
{
- var provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk", "token");
+ var provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk", "token");
var cred = provider.GetCredentials();
//case 1
@@ -336,7 +336,7 @@ public void TestAuthQueryToken()
[Fact]
public void TestAuthQueryWithAdditionalHeaders()
{
- var provider = new V2.Credentials.StaticCredentialsProvide("ak", "sk");
+ var provider = new V2.Credentials.StaticCredentialsProvider("ak", "sk");
var cred = provider.GetCredentials();
//case 1