diff --git a/framework/src/Volo.Abp.BlobStoring.Aws/Volo/Abp/BlobStoring/Aws/AwsBlobProviderConfiguration.cs b/framework/src/Volo.Abp.BlobStoring.Aws/Volo/Abp/BlobStoring/Aws/AwsBlobProviderConfiguration.cs index 1b31d176c8..77919b80ef 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aws/Volo/Abp/BlobStoring/Aws/AwsBlobProviderConfiguration.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aws/Volo/Abp/BlobStoring/Aws/AwsBlobProviderConfiguration.cs @@ -62,6 +62,15 @@ public class AwsBlobProviderConfiguration set => _containerConfiguration.SetConfiguration(AwsBlobProviderConfigurationNames.Region, Check.NotNull(value, nameof(value))); } + /// + /// Custom service URL for S3-compatible APIs (e.g., MinIO, DigitalOcean Spaces). + /// If not specified, the default AWS S3 service URL will be used based on the region. + /// + public string? ServiceURL { + get => _containerConfiguration.GetConfigurationOrDefault(AwsBlobProviderConfigurationNames.ServiceURL); + set => _containerConfiguration.SetConfiguration(AwsBlobProviderConfigurationNames.ServiceURL, value); + } + /// /// This name may only contain lowercase letters, numbers, and hyphens, and must begin with a letter or a number. /// Each hyphen must be preceded and followed by a non-hyphen character. diff --git a/framework/src/Volo.Abp.BlobStoring.Aws/Volo/Abp/BlobStoring/Aws/AwsBlobProviderConfigurationNames.cs b/framework/src/Volo.Abp.BlobStoring.Aws/Volo/Abp/BlobStoring/Aws/AwsBlobProviderConfigurationNames.cs index d1b775c812..b065441462 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aws/Volo/Abp/BlobStoring/Aws/AwsBlobProviderConfigurationNames.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aws/Volo/Abp/BlobStoring/Aws/AwsBlobProviderConfigurationNames.cs @@ -14,6 +14,7 @@ public static class AwsBlobProviderConfigurationNames public const string Name = "Aws.Name"; public const string Policy = "Aws.Policy"; public const string Region = "Aws.Region"; + public const string ServiceURL = "Aws.ServiceURL"; public const string ContainerName = "Aws.ContainerName"; public const string CreateContainerIfNotExists = "Aws.CreateContainerIfNotExists"; } diff --git a/framework/src/Volo.Abp.BlobStoring.Aws/Volo/Abp/BlobStoring/Aws/DefaultAmazonS3ClientFactory.cs b/framework/src/Volo.Abp.BlobStoring.Aws/Volo/Abp/BlobStoring/Aws/DefaultAmazonS3ClientFactory.cs index dee0bf5096..dc2a7fe56b 100644 --- a/framework/src/Volo.Abp.BlobStoring.Aws/Volo/Abp/BlobStoring/Aws/DefaultAmazonS3ClientFactory.cs +++ b/framework/src/Volo.Abp.BlobStoring.Aws/Volo/Abp/BlobStoring/Aws/DefaultAmazonS3ClientFactory.cs @@ -31,30 +31,47 @@ public class DefaultAmazonS3ClientFactory : IAmazonS3ClientFactory, ITransientDe AwsBlobProviderConfiguration configuration) { var region = RegionEndpoint.GetBySystemName(configuration.Region); + var clientConfig = CreateS3ClientConfig(configuration, region); if (configuration.UseCredentials) { var awsCredentials = GetAwsCredentials(configuration); return awsCredentials == null - ? new AmazonS3Client(region) - : new AmazonS3Client(awsCredentials, region); + ? new AmazonS3Client(clientConfig) + : new AmazonS3Client(awsCredentials, clientConfig); } if (configuration.UseTemporaryCredentials) { - return new AmazonS3Client(await GetTemporaryCredentialsAsync(configuration), region); + return new AmazonS3Client(await GetTemporaryCredentialsAsync(configuration), clientConfig); } if (configuration.UseTemporaryFederatedCredentials) { return new AmazonS3Client(await GetTemporaryFederatedCredentialsAsync(configuration), - region); + clientConfig); } Check.NotNullOrWhiteSpace(configuration.AccessKeyId, nameof(configuration.AccessKeyId)); Check.NotNullOrWhiteSpace(configuration.SecretAccessKey, nameof(configuration.SecretAccessKey)); - return new AmazonS3Client(configuration.AccessKeyId, configuration.SecretAccessKey, region); + return new AmazonS3Client(configuration.AccessKeyId, configuration.SecretAccessKey, clientConfig); + } + + protected virtual AmazonS3Config CreateS3ClientConfig(AwsBlobProviderConfiguration configuration, RegionEndpoint region) + { + var clientConfig = new AmazonS3Config + { + RegionEndpoint = region + }; + + if (!configuration.ServiceURL.IsNullOrWhiteSpace()) + { + clientConfig.ServiceURL = configuration.ServiceURL; + clientConfig.ForcePathStyle = true; // Required for most S3-compatible services + } + + return clientConfig; } protected virtual AWSCredentials? GetAwsCredentials(