diff --git a/Docs/API.md b/Docs/API.md
index 50dac3190a..87fa1b82c6 100644
--- a/Docs/API.md
+++ b/Docs/API.md
@@ -2762,6 +2762,48 @@ catch(MinioException e)
}
```
+
+### GetPresignedUrlAsync(GetPresignedUrlArgs args)
+
+`Task GetPresignedUrlAsync(GetPresignedUrlArgs args)`
+
+Generates a presigned URL for HTTP operation using the giving HTTP method (GET, PUT, DELETE). Browsers/Mobile clients may point to this URL to upload objects directly to a bucket even if it is private. This presigned URL can have an associated expiration time in seconds after which it is no longer operational. The default expiry is set to 7 days.
+
+__Parameters__
+
+
+| Param | Type | Description |
+|:---------|:------------------------|:-------------------------------------------------------------------------------------|
+| ``args`` | _GetPresignedUrlArgs_ | GetPresignedUrlArgs arguments object with HTTP method, bucket, object names & expiry |
+
+| Return Type | Exceptions |
+|:------------------------------------------------------------|:-------------------------------------------------------------------|
+| ``Task`` : string contains URL to upload the object | Listed Exceptions: |
+| | ``InvalidBucketNameException`` : upon invalid bucket name |
+| | ``InvalidKeyException`` : upon an invalid access key or secret key |
+| | ``ConnectionException`` : upon connection error |
+| | ``InvalidExpiryRangeException`` : upon invalid expiry range. |
+
+
+__Example__
+
+```cs
+try
+{
+ var httpMethod = GetPresignedUrlArgs.PresignedUrlHttpMethod.Put;
+ GetPresignedUrlArgs args = GetPresignedUrlArgs(httpMethod)
+ .WithBucket("mybucket")
+ .WithObject("myobject")
+ .WithExpiry(60 * 60 * 24);
+ String url = await minioClient.GetPresignedUrlAsync(args);
+ Console.WriteLine(url);
+}
+catch(MinioException e)
+{
+ Console.WriteLine("Error occurred: " + e);
+}
+```
+
### PresignedPostPolicy(PresignedPostPolicyArgs args)
diff --git a/Minio.Examples/Cases/GetPresignedUrl.cs b/Minio.Examples/Cases/GetPresignedUrl.cs
new file mode 100644
index 0000000000..c199e78a1c
--- /dev/null
+++ b/Minio.Examples/Cases/GetPresignedUrl.cs
@@ -0,0 +1,56 @@
+/*
+ * MinIO .NET Library for Amazon S3 Compatible Cloud Storage, (C) 2017 MinIO, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using Minio.DataModel.Args;
+
+namespace Minio.Examples.Cases;
+
+public static class GetPresignedUrl
+{
+ public static async Task Run(IMinioClient client,
+ string bucketName = "my-bucket-name",
+ string objectName = "my-object-name")
+ {
+ if (client is null) throw new ArgumentNullException(nameof(client));
+
+ try
+ {
+ await GetPresignedUrlByRequest(client,
+ GetPresignedUrlArgs.PresignedUrlHttpMethod.Get, bucketName, objectName).ConfigureAwait(false);
+ await GetPresignedUrlByRequest(client,
+ GetPresignedUrlArgs.PresignedUrlHttpMethod.Put, bucketName, objectName).ConfigureAwait(false);
+ await GetPresignedUrlByRequest(client,
+ GetPresignedUrlArgs.PresignedUrlHttpMethod.Delete, bucketName, objectName).ConfigureAwait(false);
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine($"Exception {e.Message}");
+ }
+ }
+
+ private static async Task GetPresignedUrlByRequest(IMinioClient client,
+ GetPresignedUrlArgs.PresignedUrlHttpMethod requestMethod,
+ string bucketName = "my-bucket-name",
+ string objectName = "my-object-name")
+ {
+ var args = new GetPresignedUrlArgs(requestMethod)
+ .WithBucket(bucketName)
+ .WithObject(objectName)
+ .WithExpiry(1000);
+ var presignedUrl = await client.GetPresignedUrlAsync(args).ConfigureAwait(false);
+ Console.WriteLine($"Presigned '{requestMethod}' URL: {presignedUrl}");
+ }
+}
diff --git a/Minio.Examples/Program.cs b/Minio.Examples/Program.cs
index 955d1000dd..53b9abdf5f 100644
--- a/Minio.Examples/Program.cs
+++ b/Minio.Examples/Program.cs
@@ -20,6 +20,7 @@
using System.Security.Cryptography;
using System.Text;
using Minio.DataModel;
+using Minio.DataModel.Args;
using Minio.DataModel.Encryption;
using Minio.DataModel.Notification;
using Minio.DataModel.ObjectLock;
@@ -261,6 +262,9 @@ await SetBucketReplication.Run(minioClient, bucketName, destBucketName, replicat
// Get the presigned url for a PUT object request
await PresignedPutObject.Run(minioClient, bucketName, objectName).ConfigureAwait(false);
+
+ // Get the presigned url's of an object for HTTP methods
+ await GetPresignedUrl.Run(minioClient, bucketName, objectName).ConfigureAwait(false);
// Delete the list of objects
await RemoveObjects.Run(minioClient, bucketName, objectsList).ConfigureAwait(false);
diff --git a/Minio.Functional.Tests/FunctionalTest.cs b/Minio.Functional.Tests/FunctionalTest.cs
index 84f02cb53b..2c73723c6e 100644
--- a/Minio.Functional.Tests/FunctionalTest.cs
+++ b/Minio.Functional.Tests/FunctionalTest.cs
@@ -45,280 +45,8 @@
namespace Minio.Functional.Tests;
[SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Keep private const lowercase")]
-public static class FunctionalTest
+public static partial class FunctionalTest
{
- private const int KB = 1024;
- private const int MB = 1024 * 1024;
- private const int GB = 1024 * 1024 * 1024;
-
- private const string dataFile1B = "datafile-1-b";
-
- private const string dataFile10KB = "datafile-10-kB";
- private const string dataFile6MB = "datafile-6-MB";
-
- private const string makeBucketSignature =
- "Task MakeBucketAsync(string bucketName, string location = 'us-east-1', CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string listBucketsSignature =
- "Task ListBucketsAsync(CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string bucketExistsSignature =
- "Task BucketExistsAsync(string bucketName, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string removeBucketSignature =
- "Task RemoveBucketAsync(string bucketName, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string listObjectsSignature =
- "IObservable- ListObjectsAsync(string bucketName, string prefix = null, bool recursive = false, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string putObjectSignature =
- "Task PutObjectAsync(PutObjectArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string getObjectSignature =
- "Task GetObjectAsync(GetObjectArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string listIncompleteUploadsSignature =
- "IObservable ListIncompleteUploads(ListIncompleteUploads args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string listenBucketNotificationsSignature =
- "IObservable ListenBucketNotificationsAsync(ListenBucketNotificationsArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string listenNotificationsSignature =
- "IObservable ListenNotifications(ListenBucketNotificationsArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string copyObjectSignature =
- "Task CopyObjectAsync(CopyObjectArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string statObjectSignature =
- "Task StatObjectAsync(StatObjectArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string removeObjectSignature1 =
- "Task RemoveObjectAsync(RemoveObjectArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string removeObjectSignature2 =
- "Task> RemoveObjectsAsync(RemoveObjectsArgs, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string removeIncompleteUploadSignature =
- "Task RemoveIncompleteUploadAsync(RemoveIncompleteUploadArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string presignedPutObjectSignature =
- "Task PresignedPutObjectAsync(PresignedPutObjectArgs args)";
-
- private const string presignedGetObjectSignature =
- "Task PresignedGetObjectAsync(PresignedGetObjectArgs args)";
-
- private const string presignedPostPolicySignature =
- "Task> PresignedPostPolicyAsync(PresignedPostPolicyArgs args)";
-
- private const string getBucketPolicySignature =
- "Task GetPolicyAsync(GetPolicyArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string setBucketPolicySignature =
- "Task SetPolicyAsync(SetPolicyArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string getBucketNotificationSignature =
- "Task GetBucketNotificationAsync(GetBucketNotificationsArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string setBucketNotificationSignature =
- "Task SetBucketNotificationAsync(SetBucketNotificationsArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string removeAllBucketsNotificationSignature =
- "Task RemoveAllBucketNotificationsAsync(RemoveAllBucketNotifications args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string setBucketEncryptionSignature =
- "Task SetBucketEncryptionAsync(SetBucketEncryptionArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string getBucketEncryptionSignature =
- "Task GetBucketEncryptionAsync(GetBucketEncryptionArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string removeBucketEncryptionSignature =
- "Task RemoveBucketEncryptionAsync(RemoveBucketEncryptionArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string selectObjectSignature =
- "Task SelectObjectContentAsync(SelectObjectContentArgs args,CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string setObjectLegalHoldSignature =
- "Task SetObjectLegalHoldAsync(SetObjectLegalHoldArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string getObjectLegalHoldSignature =
- "Task GetObjectLegalHoldAsync(GetObjectLegalHoldArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string setObjectLockConfigurationSignature =
- "Task SetObjectLockConfigurationAsync(SetObjectLockConfigurationArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string getObjectLockConfigurationSignature =
- "Task GetObjectLockConfigurationAsync(GetObjectLockConfigurationArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string deleteObjectLockConfigurationSignature =
- "Task RemoveObjectLockConfigurationAsync(GetObjectLockConfigurationArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string getBucketTagsSignature =
- "Task GetBucketTagsAsync(GetBucketTagsArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string setBucketTagsSignature =
- "Task SetBucketTagsAsync(SetBucketTagsArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string deleteBucketTagsSignature =
- "Task RemoveBucketTagsAsync(RemoveBucketTagsArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string setVersioningSignature =
- "Task SetVersioningAsync(SetVersioningArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string getVersioningSignature =
- "Task GetVersioningAsync(GetVersioningArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string removeVersioningSignature =
- "Task RemoveBucketTagsAsync(RemoveBucketTagsArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string getObjectTagsSignature =
- "Task GetObjectTagsAsync(GetObjectTagsArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string setObjectTagsSignature =
- "Task SetObjectTagsAsync(SetObjectTagsArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string deleteObjectTagsSignature =
- "Task RemoveObjectTagsAsync(RemoveObjectTagsArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string setObjectRetentionSignature =
- "Task SetObjectRetentionAsync(SetObjectRetentionArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string getObjectRetentionSignature =
- "Task GetObjectRetentionAsync(GetObjectRetentionArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string clearObjectRetentionSignature =
- "Task ClearObjectRetentionAsync(ClearObjectRetentionArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string getBucketLifecycleSignature =
- "Task GetBucketLifecycleAsync(GetBucketLifecycleArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string setBucketLifecycleSignature =
- "Task SetBucketLifecycleAsync(SetBucketLifecycleArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private const string deleteBucketLifecycleSignature =
- "Task RemoveBucketLifecycleAsync(RemoveBucketLifecycleArgs args, CancellationToken cancellationToken = default(CancellationToken))";
-
- private static readonly Random rnd = new();
-
- private static readonly RandomStreamGenerator rsg = new(100 * MB);
-
- private static string Bash(string cmd)
- {
- var Replacements = new Dictionary
- (StringComparer.Ordinal)
- {
- { "$", "\\$" },
- { "(", "\\(" },
- { ")", "\\)" },
- { "{", "\\{" },
- { "}", "\\}" },
- { "[", "\\[" },
- { "]", "\\]" },
- { "@", "\\@" },
- { "%", "\\%" },
- { "&", "\\&" },
- { "#", "\\#" },
- { "+", "\\+" }
- };
-
- foreach (var toReplace in Replacements.Keys)
- cmd = cmd.Replace(toReplace, Replacements[toReplace], StringComparison.Ordinal);
- var cmdNoReturn = cmd + " >/dev/null 2>&1";
-
- using var process = new Process
- {
- StartInfo = new ProcessStartInfo
- {
- FileName = "/bin/bash",
- Arguments = $"-c \"{cmdNoReturn}\"",
- UseShellExecute = false,
- RedirectStandardOutput = true,
- CreateNoWindow = true
- }
- };
-
- _ = process.Start();
- var result = process.StandardOutput.ReadLine();
- process.WaitForExit();
-
- return result;
- }
-
- // Create a file of given size from random byte array or optionally create a symbolic link
- // to the dataFileName residing in MINT_DATA_DIR
- private static string CreateFile(int size, string dataFileName = null)
- {
- var fileName = GetRandomName();
-
- if (!IsMintEnv())
- {
- var data = new byte[size];
- rnd.NextBytes(data);
-
- File.WriteAllBytes(fileName, data);
- return GetFilePath(fileName);
- }
-
- return GetFilePath(dataFileName);
- }
-
- public static string GetRandomObjectName(int length = 5)
- {
- // Server side does not allow the following characters in object names
- // '-', '_', '.', '/', '*'
-#if NET6_0_OR_GREATER
- var characters = "abcd+%$#@&{}[]()";
-#else
- var characters = "abcdefgh+%$#@&";
-#endif
- var result = new StringBuilder(length);
-
- for (var i = 0; i < length; i++) result.Append(characters[rnd.Next(characters.Length)]);
- return result.ToString();
- }
-
- // Generate a random string
- public static string GetRandomName(int length = 5)
- {
- var characters = "0123456789abcdefghijklmnopqrstuvwxyz";
- if (length > 50) length = 50;
-
- var result = new StringBuilder(length);
- for (var i = 0; i < length; i++) _ = result.Append(characters[rnd.Next(characters.Length)]);
-
- return "minio-dotnet-example-" + result;
- }
-
- internal static void GenerateRandom500MB_File(string fileName)
- {
- using var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true);
- var fileSize = 500L * 1024 * 1024;
- var segments = fileSize / 10000;
- var last_seg = fileSize % 10000;
- using var br = new BinaryWriter(fs);
-
- for (long i = 0; i < segments; i++)
- br.Write(new byte[10000]);
-
- br.Write(new byte[last_seg]);
- br.Close();
- }
-
- // Return true if running in Mint mode
- public static bool IsMintEnv()
- {
- return !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("MINT_DATA_DIR"));
- }
-
- // Get full path of file
- public static string GetFilePath(string fileName)
- {
- var dataDir = Environment.GetEnvironmentVariable("MINT_DATA_DIR");
- if (!string.IsNullOrEmpty(dataDir)) return $"{dataDir}/{fileName}";
-
- var path = Directory.GetCurrentDirectory();
- return $"{path}/{fileName}";
- }
-
internal static Task RunCoreTests(IMinioClient minioClient)
{
ConcurrentBag coreTestsTasks = new()
@@ -340,6 +68,8 @@ internal static Task RunCoreTests(IMinioClient minioClient)
// Test Presigned Get/Put operations
PresignedGetObject_Test1(minioClient),
PresignedPutObject_Test1(minioClient),
+ GetPresignedUrl_Get_Test1(minioClient),
+ GetPresignedUrl_Put_Delete_Test1(minioClient),
// Test incomplete uploads
ListIncompleteUpload_Test1(minioClient),
@@ -1321,6 +1051,13 @@ internal static async Task UploadObjectAsync(IMinioClient minio, string url, str
await minio.WrapperPutAsync(url, stream).ConfigureAwait(false);
}
+ internal static async Task DeleteObjectAsync(IMinioClient minio, string url)
+ {
+ using var response = await minio.WrapperDeleteAsync(url).ConfigureAwait(false);
+ if (string.IsNullOrEmpty(Convert.ToString(response.Content)) || HttpStatusCode.OK != response.StatusCode)
+ throw new InvalidOperationException("Unable to delete via presigned URL" + nameof(response.Content));
+ }
+
internal static async Task PresignedPostPolicy_Test1(IMinioClient minio)
{
var startTime = DateTime.Now;
@@ -5951,6 +5688,375 @@ internal static async Task PresignedPutObject_Test2(IMinioClient minio)
#endregion
+ #region Get Presigned Url
+
+ internal static async Task GetPresignedUrl_Get_Test1(IMinioClient minio)
+ {
+ var startTime = DateTime.Now;
+ var bucketName = GetRandomName(15);
+ var objectName = GetRandomObjectName(10);
+ var expiresInt = 1000;
+ var downloadFile = GetRandomObjectName(10);
+
+ var args = new Dictionary
+ (StringComparer.Ordinal)
+ {
+ { "bucketName", bucketName },
+ { "objectName", objectName },
+ { "expiresInt", expiresInt.ToString(CultureInfo.InvariantCulture) }
+ };
+ try
+ {
+ await Setup_Test(minio, bucketName).ConfigureAwait(false);
+ using (var filestream = rsg.GenerateStreamFromSeed(1 * KB))
+ {
+ var putObjectArgs = new PutObjectArgs()
+ .WithBucket(bucketName)
+ .WithObject(objectName)
+ .WithStreamData(filestream)
+ .WithObjectSize(filestream.Length);
+
+ _ = await minio.PutObjectAsync(putObjectArgs).ConfigureAwait(false);
+ }
+
+ var statObjectArgs = new StatObjectArgs()
+ .WithBucket(bucketName)
+ .WithObject(objectName);
+ var stats = await minio.StatObjectAsync(statObjectArgs).ConfigureAwait(false);
+
+ var httpMethod = GetPresignedUrlArgs.PresignedUrlHttpMethod.Get;
+ var preArgs = new GetPresignedUrlArgs(httpMethod)
+ .WithBucket(bucketName)
+ .WithObject(objectName)
+ .WithExpiry(expiresInt);
+ var presigned_url = await minio.GetPresignedUrlAsync(preArgs).ConfigureAwait(false);
+
+ await DownloadObjectAsync(minio, presigned_url, downloadFile).ConfigureAwait(false);
+ var writtenInfo = new FileInfo(downloadFile);
+ var file_read_size = writtenInfo.Length;
+ // Compare the size of the file downloaded using the generated
+ // presigned_url (expected value) and the actual object size on the server
+ Assert.AreEqual(file_read_size, stats.Size);
+ new MintLogger("GetPresignedUrl_Get_Test1", getPresignedUrlSignature,
+ "Tests whether GetPresignedUrl url retrieves object from bucket", TestStatus.PASS,
+ DateTime.Now - startTime, args: args).Log();
+ }
+ catch (Exception ex)
+ {
+ new MintLogger("GetPresignedUrl_Get_Test1", getPresignedUrlSignature,
+ "Tests whether GetPresignedUrl url retrieves object from bucket", TestStatus.FAIL,
+ DateTime.Now - startTime, ex.Message, ex.ToString(), args: args).Log();
+ throw;
+ }
+ finally
+ {
+ File.Delete(downloadFile);
+ await TearDown(minio, bucketName).ConfigureAwait(false);
+ }
+ }
+
+ internal static async Task GetPresignedUrl_Get_Test2(IMinioClient minio)
+ {
+ var startTime = DateTime.Now;
+ var bucketName = GetRandomName(15);
+ var objectName = GetRandomObjectName(10);
+ var expiresInt = 0;
+ var args = new Dictionary
+ (StringComparer.Ordinal)
+ {
+ { "bucketName", bucketName },
+ { "objectName", objectName },
+ { "expiresInt", expiresInt.ToString(CultureInfo.InvariantCulture) }
+ };
+ try
+ {
+ await Setup_Test(minio, bucketName).ConfigureAwait(false);
+ using (var filestream = rsg.GenerateStreamFromSeed(1 * KB))
+ {
+ var putObjectArgs = new PutObjectArgs()
+ .WithBucket(bucketName)
+ .WithObject(objectName)
+ .WithStreamData(filestream)
+ .WithObjectSize(filestream.Length);
+
+ _ = await minio.PutObjectAsync(putObjectArgs).ConfigureAwait(false);
+ }
+
+ var statObjectArgs = new StatObjectArgs()
+ .WithBucket(bucketName)
+ .WithObject(objectName);
+ var stats = await minio.StatObjectAsync(statObjectArgs).ConfigureAwait(false);
+ var httpMethod = GetPresignedUrlArgs.PresignedUrlHttpMethod.Get;
+ var preArgs = new GetPresignedUrlArgs(httpMethod)
+ .WithBucket(bucketName)
+ .WithObject(objectName)
+ .WithExpiry(0);
+ var presigned_url = await minio.GetPresignedUrlAsync(preArgs).ConfigureAwait(false);
+ throw new InvalidOperationException(
+ "GetPresignedUrlAsync expected to throw an InvalidExpiryRangeException.");
+ }
+ catch (InvalidExpiryRangeException)
+ {
+ new MintLogger("GetPresignedUrl_Get_Test2", getPresignedUrlSignature,
+ "Tests whether GetPresignedUrl url retrieves object from bucket when invalid expiry is set.",
+ TestStatus.PASS, DateTime.Now - startTime, args: args).Log();
+ }
+ catch (InvalidOperationException ex)
+ {
+ new MintLogger("GetPresignedUrl_Get_Test2", getPresignedUrlSignature,
+ "Tests whether GetPresignedUrl url retrieves object from bucket when invalid expiry is set.",
+ TestStatus.FAIL, DateTime.Now - startTime, ex.Message, ex.ToString(), args: args).Log();
+ throw;
+ }
+ catch (Exception ex)
+ {
+ new MintLogger("GetPresignedUrl_Get_Test2", getPresignedUrlSignature,
+ "Tests whether GetPresignedUrl url retrieves object from bucket when invalid expiry is set.",
+ TestStatus.FAIL, DateTime.Now - startTime, ex.Message, ex.ToString(), args: args).Log();
+ throw;
+ }
+ finally
+ {
+ await TearDown(minio, bucketName).ConfigureAwait(false);
+ }
+ }
+
+ internal static async Task GetPresignedUrl_Get_Test3(IMinioClient minio)
+ {
+ var startTime = DateTime.Now;
+ var bucketName = GetRandomName(15);
+ var objectName = GetRandomObjectName(10);
+ var expiresInt = 1000;
+ var reqDate = DateTime.UtcNow.AddSeconds(-50);
+ var downloadFile = GetRandomObjectName(10);
+ var args = new Dictionary
+ (StringComparer.Ordinal)
+ {
+ { "bucketName", bucketName },
+ { "objectName", objectName },
+ { "expiresInt", expiresInt.ToString(CultureInfo.InvariantCulture) },
+ {
+ "reqParams",
+ "response-content-type:application/json,response-content-disposition:attachment;filename= MyDoc u m e nt.json ;"
+ },
+ { "reqDate", reqDate.ToString(CultureInfo.InvariantCulture) }
+ };
+ try
+ {
+ await Setup_Test(minio, bucketName).ConfigureAwait(false);
+ using (var filestream = rsg.GenerateStreamFromSeed(1 * KB))
+ {
+ var putObjectArgs = new PutObjectArgs()
+ .WithBucket(bucketName)
+ .WithObject(objectName)
+ .WithStreamData(filestream)
+ .WithObjectSize(filestream.Length);
+
+ _ = await minio.PutObjectAsync(putObjectArgs).ConfigureAwait(false);
+ }
+
+ var statObjectArgs = new StatObjectArgs()
+ .WithBucket(bucketName)
+ .WithObject(objectName);
+ var stats = await minio.StatObjectAsync(statObjectArgs).ConfigureAwait(false);
+ var reqParams = new Dictionary
+ (StringComparer.Ordinal)
+ {
+ ["response-content-type"] = "application/json",
+ ["response-content-disposition"] = "attachment;filename= MyDoc u m e nt.json ;"
+ };
+ var httpMethod = GetPresignedUrlArgs.PresignedUrlHttpMethod.Get;
+ var preArgs = new GetPresignedUrlArgs(httpMethod)
+ .WithBucket(bucketName)
+ .WithObject(objectName)
+ .WithExpiry(1000)
+ .WithHeaders(reqParams)
+ .WithRequestDate(reqDate);
+ var presigned_url = await minio.GetPresignedUrlAsync(preArgs).ConfigureAwait(false);
+
+ using var response = await minio.WrapperGetAsync(presigned_url).ConfigureAwait(false);
+ if (response.StatusCode != HttpStatusCode.OK ||
+ string.IsNullOrEmpty(Convert.ToString(response.Content, CultureInfo.InvariantCulture)))
+ throw new InvalidOperationException("Unable to download via presigned URL " + nameof(response.Content));
+
+ Assert.IsTrue(response.Content.Headers.GetValues("Content-Type")
+ .Contains(reqParams["response-content-type"], StringComparer.Ordinal));
+ Assert.IsTrue(response.Content.Headers.GetValues("Content-Disposition")
+ .Contains(reqParams["response-content-disposition"], StringComparer.Ordinal));
+ Assert.IsTrue(response.Content.Headers.GetValues("Content-Length")
+ .Contains(stats.Size.ToString(CultureInfo.InvariantCulture), StringComparer.Ordinal));
+
+ using (var fs = new FileStream(downloadFile, FileMode.CreateNew))
+ {
+ await response.Content.CopyToAsync(fs).ConfigureAwait(false);
+ }
+
+ var writtenInfo = new FileInfo(downloadFile);
+ var file_read_size = writtenInfo.Length;
+
+ // Compare the size of the file downloaded with the generated
+ // presigned_url (expected) and the actual object size on the server
+ Assert.AreEqual(file_read_size, stats.Size);
+ new MintLogger("GetPresignedUrl_Get_Test3", getPresignedUrlSignature,
+ "Tests whether GetPresignedUrl url retrieves object from bucket when override response headers sent",
+ TestStatus.PASS, DateTime.Now - startTime, args: args).Log();
+ }
+ catch (Exception ex)
+ {
+ new MintLogger("GetPresignedUrl_Get_Test3", getPresignedUrlSignature,
+ "Tests whether GetPresignedUrl url retrieves object from bucket when override response headers sent",
+ TestStatus.FAIL, DateTime.Now - startTime, ex.Message, ex.ToString(), args: args).Log();
+ throw;
+ }
+ finally
+ {
+ File.Delete(downloadFile);
+ await TearDown(minio, bucketName).ConfigureAwait(false);
+ }
+ }
+
+ internal static async Task GetPresignedUrl_Put_Delete_Test1(IMinioClient minio)
+ {
+ var startTime = DateTime.Now;
+ var bucketName = GetRandomName(15);
+ var objectName = GetRandomObjectName(10);
+ var expiresInt = 1000;
+ var fileName = CreateFile(10 * KB, dataFile10KB);
+
+ var args = new Dictionary
+ (StringComparer.Ordinal)
+ {
+ { "bucketName", bucketName },
+ { "objectName", objectName },
+ { "expiresInt", expiresInt.ToString(CultureInfo.InvariantCulture) }
+ };
+ try
+ {
+ await Setup_Test(minio, bucketName).ConfigureAwait(false);
+ // Upload with presigned url
+ var httpMethod = GetPresignedUrlArgs.PresignedUrlHttpMethod.Put;
+ var presignedPutObjectArgs = new GetPresignedUrlArgs(httpMethod)
+ .WithBucket(bucketName)
+ .WithObject(objectName)
+ .WithExpiry(1000);
+ var presigned_url = await minio.GetPresignedUrlAsync(presignedPutObjectArgs).ConfigureAwait(false);
+ await UploadObjectAsync(minio, presigned_url, fileName).ConfigureAwait(false);
+ // Get stats for object from server
+ var statObjectArgs = new StatObjectArgs()
+ .WithBucket(bucketName)
+ .WithObject(objectName);
+ var stats = await minio.StatObjectAsync(statObjectArgs).ConfigureAwait(false);
+ // Compare with file used for upload
+ var writtenInfo = new FileInfo(fileName);
+ var file_written_size = writtenInfo.Length;
+ Assert.AreEqual(file_written_size, stats.Size);
+ new MintLogger("GetPresignedUrl_Put_Delete_Test1", getPresignedUrlSignature,
+ "Tests whether GetPresignedUrl url uploads object to bucket", TestStatus.PASS,
+ DateTime.Now - startTime, args: args).Log();
+
+ // Delete with presigned url
+ httpMethod = GetPresignedUrlArgs.PresignedUrlHttpMethod.Delete;
+ presignedPutObjectArgs = new GetPresignedUrlArgs(httpMethod)
+ .WithBucket(bucketName)
+ .WithObject(objectName)
+ .WithExpiry(1000);
+ presigned_url = await minio.GetPresignedUrlAsync(presignedPutObjectArgs).ConfigureAwait(false);
+ await DeleteObjectAsync(minio, presigned_url).ConfigureAwait(false);
+
+ new MintLogger("GetPresignedUrl_Put_Delete_Test1", getPresignedUrlSignature,
+ "Tests whether GetPresignedUrl url deletes object from bucket", TestStatus.PASS,
+ DateTime.Now - startTime, args: args).Log();
+ }
+ catch (Exception ex)
+ {
+ new MintLogger("GetPresignedUrl_Put_Delete_Test1", getPresignedUrlSignature,
+ "Tests whether GetPresignedUrl url uploads object to bucket", TestStatus.FAIL,
+ DateTime.Now - startTime, ex.Message, ex.ToString(), args: args).Log();
+ throw;
+ }
+ finally
+ {
+ await TearDown(minio, bucketName).ConfigureAwait(false);
+ if (!IsMintEnv()) File.Delete(fileName);
+ }
+ }
+
+ internal static async Task GetPresignedUrl_Put_Delete_Test2(IMinioClient minio)
+ {
+ var startTime = DateTime.Now;
+ var bucketName = GetRandomName(15);
+ var objectName = GetRandomObjectName(10);
+ var expiresInt = 0;
+
+ var args = new Dictionary
+ (StringComparer.Ordinal)
+ {
+ { "bucketName", bucketName },
+ { "objectName", objectName },
+ { "expiresInt", expiresInt.ToString(CultureInfo.InvariantCulture) }
+ };
+ try
+ {
+ await Setup_Test(minio, bucketName).ConfigureAwait(false);
+ using (var filestream = rsg.GenerateStreamFromSeed(1 * KB))
+ {
+ var putObjectArgs = new PutObjectArgs()
+ .WithBucket(bucketName)
+ .WithObject(objectName)
+ .WithStreamData(filestream)
+ .WithObjectSize(filestream.Length);
+
+ _ = await minio.PutObjectAsync(putObjectArgs).ConfigureAwait(false);
+ }
+
+ var statObjectArgs = new StatObjectArgs()
+ .WithBucket(bucketName)
+ .WithObject(objectName);
+ var stats = await minio.StatObjectAsync(statObjectArgs).ConfigureAwait(false);
+ var httpMethod = GetPresignedUrlArgs.PresignedUrlHttpMethod.Put;
+ var presignedPutObjectArgs = new GetPresignedUrlArgs(httpMethod)
+ .WithBucket(bucketName)
+ .WithObject(objectName)
+ .WithExpiry(0);
+ var presigned_url = await minio.GetPresignedUrlAsync(presignedPutObjectArgs).ConfigureAwait(false);
+ new MintLogger("GetPresignedUrl_Put_Delete_Test2", getPresignedUrlSignature,
+ "Tests whether GetPresignedUrl url retrieves object from bucket when invalid expiry is set.",
+ TestStatus.PASS, DateTime.Now - startTime, args: args).Log();
+
+ // Delete with presigned url
+ httpMethod = GetPresignedUrlArgs.PresignedUrlHttpMethod.Delete;
+ presignedPutObjectArgs = new GetPresignedUrlArgs(httpMethod)
+ .WithBucket(bucketName)
+ .WithObject(objectName)
+ .WithExpiry(1000);
+ presigned_url = await minio.GetPresignedUrlAsync(presignedPutObjectArgs).ConfigureAwait(false);
+ await DeleteObjectAsync(minio, presigned_url).ConfigureAwait(false);
+
+ new MintLogger("GetPresignedUrl_Put_Delete_Test1", getPresignedUrlSignature,
+ "Tests whether GetPresignedUrl url deletes object from bucket", TestStatus.PASS,
+ DateTime.Now - startTime, args: args).Log();
+ }
+ catch (InvalidExpiryRangeException)
+ {
+ new MintLogger("GetPresignedUrl_Put_Delete_Test2", getPresignedUrlSignature,
+ "Tests whether GetPresignedUrl url retrieves object from bucket when invalid expiry is set.",
+ TestStatus.PASS, DateTime.Now - startTime, args: args).Log();
+ }
+ catch (Exception ex)
+ {
+ new MintLogger("GetPresignedUrl_Put_Delete_Test2", getPresignedUrlSignature,
+ "Tests whether GetPresignedUrl url retrieves object from bucket when invalid expiry is set.",
+ TestStatus.FAIL, DateTime.Now - startTime, ex.Message, ex.ToString(), args: args).Log();
+ throw;
+ }
+ finally
+ {
+ await TearDown(minio, bucketName).ConfigureAwait(false);
+ }
+ }
+
+ #endregion
+
#region List Incomplete Upload
internal static async Task ListIncompleteUpload_Test1(IMinioClient minio)
diff --git a/Minio.Functional.Tests/FunctionalTests-setup.cs b/Minio.Functional.Tests/FunctionalTests-setup.cs
new file mode 100644
index 0000000000..3c22dbd00f
--- /dev/null
+++ b/Minio.Functional.Tests/FunctionalTests-setup.cs
@@ -0,0 +1,302 @@
+/*
+ * MinIO .NET Library for Amazon S3 Compatible Cloud Storage,
+ * (C) 2017-2021 MinIO, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
+using System.Text;
+
+namespace Minio.Functional.Tests;
+
+[SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Keep private const lowercase")]
+[SuppressMessage("Design", "MA0048:File name must match type name")]
+public static partial class FunctionalTest
+{
+ private const int KB = 1024;
+ private const int MB = 1024 * 1024;
+ private const int GB = 1024 * 1024 * 1024;
+
+ private const string dataFile1B = "datafile-1-b";
+
+ private const string dataFile10KB = "datafile-10-kB";
+ private const string dataFile6MB = "datafile-6-MB";
+
+ private const string makeBucketSignature =
+ "Task MakeBucketAsync(string bucketName, string location = 'us-east-1', CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string listBucketsSignature =
+ "Task ListBucketsAsync(CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string bucketExistsSignature =
+ "Task BucketExistsAsync(string bucketName, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string removeBucketSignature =
+ "Task RemoveBucketAsync(string bucketName, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string listObjectsSignature =
+ "IObservable
- ListObjectsAsync(string bucketName, string prefix = null, bool recursive = false, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string putObjectSignature =
+ "Task PutObjectAsync(PutObjectArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string getObjectSignature =
+ "Task GetObjectAsync(GetObjectArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string listIncompleteUploadsSignature =
+ "IObservable ListIncompleteUploads(ListIncompleteUploads args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string listenBucketNotificationsSignature =
+ "IObservable ListenBucketNotificationsAsync(ListenBucketNotificationsArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string listenNotificationsSignature =
+ "IObservable ListenNotifications(ListenBucketNotificationsArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string copyObjectSignature =
+ "Task CopyObjectAsync(CopyObjectArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string statObjectSignature =
+ "Task StatObjectAsync(StatObjectArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string removeObjectSignature1 =
+ "Task RemoveObjectAsync(RemoveObjectArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string removeObjectSignature2 =
+ "Task> RemoveObjectsAsync(RemoveObjectsArgs, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string removeIncompleteUploadSignature =
+ "Task RemoveIncompleteUploadAsync(RemoveIncompleteUploadArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string presignedPutObjectSignature =
+ "Task PresignedPutObjectAsync(PresignedPutObjectArgs args)";
+
+ private const string presignedGetObjectSignature =
+ "Task PresignedGetObjectAsync(PresignedGetObjectArgs args)";
+
+ private const string getPresignedUrlSignature =
+ "Task GetPresignedUrlAsync(GetPresignedUrlArgs args)";
+
+ private const string presignedPostPolicySignature =
+ "Task> PresignedPostPolicyAsync(PresignedPostPolicyArgs args)";
+
+ private const string getBucketPolicySignature =
+ "Task GetPolicyAsync(GetPolicyArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string setBucketPolicySignature =
+ "Task SetPolicyAsync(SetPolicyArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string getBucketNotificationSignature =
+ "Task GetBucketNotificationAsync(GetBucketNotificationsArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string setBucketNotificationSignature =
+ "Task SetBucketNotificationAsync(SetBucketNotificationsArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string removeAllBucketsNotificationSignature =
+ "Task RemoveAllBucketNotificationsAsync(RemoveAllBucketNotifications args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string setBucketEncryptionSignature =
+ "Task SetBucketEncryptionAsync(SetBucketEncryptionArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string getBucketEncryptionSignature =
+ "Task GetBucketEncryptionAsync(GetBucketEncryptionArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string removeBucketEncryptionSignature =
+ "Task RemoveBucketEncryptionAsync(RemoveBucketEncryptionArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string selectObjectSignature =
+ "Task SelectObjectContentAsync(SelectObjectContentArgs args,CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string setObjectLegalHoldSignature =
+ "Task SetObjectLegalHoldAsync(SetObjectLegalHoldArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string getObjectLegalHoldSignature =
+ "Task GetObjectLegalHoldAsync(GetObjectLegalHoldArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string setObjectLockConfigurationSignature =
+ "Task SetObjectLockConfigurationAsync(SetObjectLockConfigurationArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string getObjectLockConfigurationSignature =
+ "Task GetObjectLockConfigurationAsync(GetObjectLockConfigurationArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string deleteObjectLockConfigurationSignature =
+ "Task RemoveObjectLockConfigurationAsync(GetObjectLockConfigurationArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string getBucketTagsSignature =
+ "Task GetBucketTagsAsync(GetBucketTagsArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string setBucketTagsSignature =
+ "Task SetBucketTagsAsync(SetBucketTagsArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string deleteBucketTagsSignature =
+ "Task RemoveBucketTagsAsync(RemoveBucketTagsArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string setVersioningSignature =
+ "Task SetVersioningAsync(SetVersioningArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string getVersioningSignature =
+ "Task GetVersioningAsync(GetVersioningArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string removeVersioningSignature =
+ "Task RemoveBucketTagsAsync(RemoveBucketTagsArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string getObjectTagsSignature =
+ "Task GetObjectTagsAsync(GetObjectTagsArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string setObjectTagsSignature =
+ "Task SetObjectTagsAsync(SetObjectTagsArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string deleteObjectTagsSignature =
+ "Task RemoveObjectTagsAsync(RemoveObjectTagsArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string setObjectRetentionSignature =
+ "Task SetObjectRetentionAsync(SetObjectRetentionArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string getObjectRetentionSignature =
+ "Task GetObjectRetentionAsync(GetObjectRetentionArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string clearObjectRetentionSignature =
+ "Task ClearObjectRetentionAsync(ClearObjectRetentionArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string getBucketLifecycleSignature =
+ "Task GetBucketLifecycleAsync(GetBucketLifecycleArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string setBucketLifecycleSignature =
+ "Task SetBucketLifecycleAsync(SetBucketLifecycleArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private const string deleteBucketLifecycleSignature =
+ "Task RemoveBucketLifecycleAsync(RemoveBucketLifecycleArgs args, CancellationToken cancellationToken = default(CancellationToken))";
+
+ private static readonly Random rnd = new();
+
+ private static readonly RandomStreamGenerator rsg = new(100 * MB);
+
+ private static string Bash(string cmd)
+ {
+ var Replacements = new Dictionary
+ (StringComparer.Ordinal)
+ {
+ { "$", "\\$" },
+ { "(", "\\(" },
+ { ")", "\\)" },
+ { "{", "\\{" },
+ { "}", "\\}" },
+ { "[", "\\[" },
+ { "]", "\\]" },
+ { "@", "\\@" },
+ { "%", "\\%" },
+ { "&", "\\&" },
+ { "#", "\\#" },
+ { "+", "\\+" }
+ };
+
+ foreach (var toReplace in Replacements.Keys)
+ cmd = cmd.Replace(toReplace, Replacements[toReplace], StringComparison.Ordinal);
+ var cmdNoReturn = cmd + " >/dev/null 2>&1";
+
+ using var process = new Process
+ {
+ StartInfo = new ProcessStartInfo
+ {
+ FileName = "/bin/bash",
+ Arguments = $"-c \"{cmdNoReturn}\"",
+ UseShellExecute = false,
+ RedirectStandardOutput = true,
+ CreateNoWindow = true
+ }
+ };
+
+ _ = process.Start();
+ var result = process.StandardOutput.ReadLine();
+ process.WaitForExit();
+
+ return result;
+ }
+
+ // Create a file of given size from random byte array or optionally create a symbolic link
+ // to the dataFileName residing in MINT_DATA_DIR
+ private static string CreateFile(int size, string dataFileName = null)
+ {
+ var fileName = GetRandomName();
+
+ if (!IsMintEnv())
+ {
+ var data = new byte[size];
+ rnd.NextBytes(data);
+
+ File.WriteAllBytes(fileName, data);
+ return GetFilePath(fileName);
+ }
+
+ return GetFilePath(dataFileName);
+ }
+
+ public static string GetRandomObjectName(int length = 5)
+ {
+ // Server side does not allow the following characters in object names
+ // '-', '_', '.', '/', '*'
+#if NET6_0_OR_GREATER
+ var characters = "abcd+%$#@&{}[]()";
+#else
+ var characters = "abcdefgh+%$#@&";
+#endif
+ var result = new StringBuilder(length);
+
+ for (var i = 0; i < length; i++) result.Append(characters[rnd.Next(characters.Length)]);
+ return result.ToString();
+ }
+
+ // Generate a random string
+ public static string GetRandomName(int length = 5)
+ {
+ var characters = "0123456789abcdefghijklmnopqrstuvwxyz";
+ if (length > 50) length = 50;
+
+ var result = new StringBuilder(length);
+ for (var i = 0; i < length; i++) _ = result.Append(characters[rnd.Next(characters.Length)]);
+
+ return "minio-dotnet-example-" + result;
+ }
+
+ internal static void GenerateRandom500MB_File(string fileName)
+ {
+ using var fs = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None, 4096, true);
+ var fileSize = 500L * 1024 * 1024;
+ var segments = fileSize / 10000;
+ var last_seg = fileSize % 10000;
+ using var br = new BinaryWriter(fs);
+
+ for (long i = 0; i < segments; i++)
+ br.Write(new byte[10000]);
+
+ br.Write(new byte[last_seg]);
+ br.Close();
+ }
+
+ // Return true if running in Mint mode
+ public static bool IsMintEnv()
+ {
+ return !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("MINT_DATA_DIR"));
+ }
+
+ // Get full path of file
+ public static string GetFilePath(string fileName)
+ {
+ var dataDir = Environment.GetEnvironmentVariable("MINT_DATA_DIR");
+ if (!string.IsNullOrEmpty(dataDir)) return $"{dataDir}/{fileName}";
+
+ var path = Directory.GetCurrentDirectory();
+ return $"{path}/{fileName}";
+ }
+}
diff --git a/Minio.Functional.Tests/Program.cs b/Minio.Functional.Tests/Program.cs
index 5fc6ed93ac..df0a4d33c2 100644
--- a/Minio.Functional.Tests/Program.cs
+++ b/Minio.Functional.Tests/Program.cs
@@ -212,6 +212,11 @@ public static async Task Main(string[] args)
functionalTestTasks.Add(FunctionalTest.PresignedPutObject_Test1(minioClient));
functionalTestTasks.Add(FunctionalTest.PresignedPutObject_Test2(minioClient));
// FunctionalTest.PresignedPostPolicy_Test1(minioClient).Wait();
+ functionalTestTasks.Add(FunctionalTest.GetPresignedUrl_Get_Test1(minioClient));
+ functionalTestTasks.Add(FunctionalTest.GetPresignedUrl_Get_Test2(minioClient));
+ functionalTestTasks.Add(FunctionalTest.GetPresignedUrl_Get_Test3(minioClient));
+ functionalTestTasks.Add(FunctionalTest.GetPresignedUrl_Put_Delete_Test1(minioClient));
+ functionalTestTasks.Add(FunctionalTest.GetPresignedUrl_Put_Delete_Test2(minioClient));
// Test incomplete uploads
functionalTestTasks.Add(FunctionalTest.ListIncompleteUpload_Test1(minioClient));
diff --git a/Minio/ApiEndpoints/IObjectOperations.cs b/Minio/ApiEndpoints/IObjectOperations.cs
index 2dc35fdd90..a07cc84bbf 100644
--- a/Minio/ApiEndpoints/IObjectOperations.cs
+++ b/Minio/ApiEndpoints/IObjectOperations.cs
@@ -293,6 +293,20 @@ IAsyncEnumerable ListIncompleteUploadsEnumAsync(ListIncompleteUploadsArg
/// When object is not found
/// When configuration XML provided is invalid
Task PresignedPutObjectAsync(PresignedPutObjectArgs args);
+
+ ///
+ /// Get Presigned Url -returns a presigned url of an object for HTTP method without credentials.URL can have a maximum expiry of
+ /// upto 7 days or a minimum of 1 second.
+ ///
+ /// GetPresignedUrlArgs Arguments Object which encapsulates HTTP method, bucket, object names, expiry
+ ///
+ /// When access or secret key is invalid
+ /// When bucket name is invalid
+ /// When object name is invalid
+ /// When bucket is not found
+ /// When object is not found
+ /// When configuration XML provided is invalid
+ Task GetPresignedUrlAsync(GetPresignedUrlArgs args);
///
/// Tests the object's existence and returns metadata about existing objects.
diff --git a/Minio/ApiEndpoints/ObjectOperations.cs b/Minio/ApiEndpoints/ObjectOperations.cs
index e523a7c2dd..87de2e6ae6 100644
--- a/Minio/ApiEndpoints/ObjectOperations.cs
+++ b/Minio/ApiEndpoints/ObjectOperations.cs
@@ -274,6 +274,31 @@ public async Task PresignedPutObjectAsync(PresignedPutObjectArgs args)
return authenticator.PresignURL(requestMessageBuilder, args.Expiry, Config.Region, Config.SessionToken);
}
+ ///
+ /// Get Presigned Url -returns a presigned url of an object for HTTP method without credentials.URL can have a maximum expiry of
+ /// upto 7 days or a minimum of 1 second.
+ ///
+ /// GetPresignedUrlArgs Arguments Object which encapsulates HTTP method, bucket, object names, expiry
+ ///
+ /// When access or secret key is invalid
+ /// When bucket name is invalid
+ /// When object name is invalid
+ /// When bucket is not found
+ /// When object is not found
+ /// When configuration XML provided is invalid
+ public async Task GetPresignedUrlAsync(GetPresignedUrlArgs args)
+ {
+ args?.Validate();
+ var requestMessageBuilder = await this.CreateRequest(args.RequestMethod, args.BucketName,
+ args.ObjectName,
+ args.Headers, // contentType
+ Convert.ToString(args.GetType(), CultureInfo.InvariantCulture), // metaData
+ Utils.ObjectToByteArray(args.RequestBody)).ConfigureAwait(false);
+ var authenticator = new V4Authenticator(Config.Secure, Config.AccessKey, Config.SecretKey, Config.Region,
+ Config.SessionToken);
+ return authenticator.PresignURL(requestMessageBuilder, args.Expiry, Config.Region, Config.SessionToken);
+ }
+
///
/// Get the configuration object for Legal Hold Status
///
diff --git a/Minio/DataModel/Args/GetPresignedUrlArgs.cs b/Minio/DataModel/Args/GetPresignedUrlArgs.cs
new file mode 100644
index 0000000000..64ba47b39b
--- /dev/null
+++ b/Minio/DataModel/Args/GetPresignedUrlArgs.cs
@@ -0,0 +1,69 @@
+/*
+ * MinIO .NET Library for Amazon S3 Compatible Cloud Storage, (C) 2020, 2021 MinIO, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+using Minio.Exceptions;
+using Minio.Helper;
+
+namespace Minio.DataModel.Args;
+
+public class GetPresignedUrlArgs : ObjectArgs
+{
+ public GetPresignedUrlArgs(PresignedUrlHttpMethod requestMethod)
+ {
+ RequestMethod = ToHttpMethod(requestMethod);
+ }
+
+ internal int Expiry { get; set; }
+ internal DateTime? RequestDate { get; set; }
+
+ internal override void Validate()
+ {
+ base.Validate();
+ if (!Utils.IsValidExpiry(Expiry))
+ throw new InvalidExpiryRangeException("Expiry range should be between 1 and " +
+ Constants.DefaultExpiryTime);
+ }
+
+ public GetPresignedUrlArgs WithExpiry(int expiry)
+ {
+ Expiry = expiry;
+ return this;
+ }
+
+ public GetPresignedUrlArgs WithRequestDate(DateTime? d)
+ {
+ RequestDate = d;
+ return this;
+ }
+
+ public enum PresignedUrlHttpMethod
+ {
+ Get,
+ Put,
+ Delete
+ }
+
+ private static HttpMethod ToHttpMethod(PresignedUrlHttpMethod method)
+ {
+ return method switch
+ {
+ PresignedUrlHttpMethod.Get => HttpMethod.Get,
+ PresignedUrlHttpMethod.Put => HttpMethod.Put,
+ PresignedUrlHttpMethod.Delete => HttpMethod.Delete,
+ _ => throw new ArgumentOutOfRangeException(nameof(method), method, null)
+ };
+ }
+}
diff --git a/Minio/DataModel/Args/PresignedGetObjectArgs.cs b/Minio/DataModel/Args/PresignedGetObjectArgs.cs
index 872e8aecb6..2febd5b7b7 100644
--- a/Minio/DataModel/Args/PresignedGetObjectArgs.cs
+++ b/Minio/DataModel/Args/PresignedGetObjectArgs.cs
@@ -33,7 +33,7 @@ internal override void Validate()
{
base.Validate();
if (!Utils.IsValidExpiry(Expiry))
- throw new InvalidExpiryRangeException("expiry range should be between 1 and " +
+ throw new InvalidExpiryRangeException("Expiry range should be between 1 and " +
Constants.DefaultExpiryTime);
}
diff --git a/Minio/IMinioClient.cs b/Minio/IMinioClient.cs
index 6700881251..ba98acb9d7 100644
--- a/Minio/IMinioClient.cs
+++ b/Minio/IMinioClient.cs
@@ -30,5 +30,6 @@ public interface IMinioClient : IBucketOperations, IObjectOperations, IDisposabl
void SetTraceOff();
void SetTraceOn(IRequestLogger logger = null);
Task WrapperGetAsync(Uri uri);
+ Task WrapperDeleteAsync(Uri uri);
Task WrapperPutAsync(Uri uri, StreamContent strm);
}
diff --git a/Minio/MinioClient.cs b/Minio/MinioClient.cs
index 4b30cd710d..4223b94c54 100644
--- a/Minio/MinioClient.cs
+++ b/Minio/MinioClient.cs
@@ -67,6 +67,14 @@ public Task WrapperPutAsync(Uri uri, StreamContent strm)
return Task.Run(async () => await Config.HttpClient.PutAsync(uri, strm).ConfigureAwait(false));
}
+ ///
+ /// Runs httpClient's DeleteAsync method
+ ///
+ public Task WrapperDeleteAsync(Uri uri)
+ {
+ return Config.HttpClient.DeleteAsync(uri);
+ }
+
///
/// Sets HTTP tracing On.Writes output to Console
///
diff --git a/Minio/RequestExtensions.cs b/Minio/RequestExtensions.cs
index 5cd32ec522..d26a984ae7 100644
--- a/Minio/RequestExtensions.cs
+++ b/Minio/RequestExtensions.cs
@@ -13,6 +13,9 @@ namespace Minio;
public static class RequestExtensions
{
+ ///
+ /// Runs httpClient's GetObjectAsync method
+ ///
[SuppressMessage("Design", "CA1054:URI-like parameters should not be strings",
Justification = "This is done in the interface. String is provided here for convenience")]
public static Task WrapperGetAsync(this IMinioClient minioClient, string url)
@@ -22,6 +25,18 @@ public static Task WrapperGetAsync(this IMinioClient minioC
: minioClient.WrapperGetAsync(new Uri(url));
}
+ ///
+ /// Runs httpClient's DeleteObjectAsync method
+ ///
+ [SuppressMessage("Design", "CA1054:URI-like parameters should not be strings",
+ Justification = "This is done in the interface. String is provided here for convenience")]
+ public static Task WrapperDeleteAsync(this IMinioClient minioClient, string url)
+ {
+ return minioClient is null
+ ? throw new ArgumentNullException(nameof(minioClient))
+ : minioClient.WrapperDeleteAsync(new Uri(url));
+ }
+
///
/// Runs httpClient's PutObjectAsync method
///