diff --git a/services/storage-service-net/src/StorageService.API/Application/Commands/MultipartUpload/CompleteMultipartUploadCommandHandler.cs b/services/storage-service-net/src/StorageService.API/Application/Commands/MultipartUpload/CompleteMultipartUploadCommandHandler.cs index d25f4210..42776e86 100644 --- a/services/storage-service-net/src/StorageService.API/Application/Commands/MultipartUpload/CompleteMultipartUploadCommandHandler.cs +++ b/services/storage-service-net/src/StorageService.API/Application/Commands/MultipartUpload/CompleteMultipartUploadCommandHandler.cs @@ -93,7 +93,9 @@ public class CompleteMultipartUploadCommandHandler fileSizeBytes: upload.TotalSizeBytes, userId: upload.UserId, provider: provider.ProviderType, - accessLevel: FileAccessLevel.Private // TODO: Get from upload metadata + // EN: Multipart aggregate currently does not persist access-level metadata. + // VI: Aggregate multipart hiện chưa lưu metadata access-level. + accessLevel: FileAccessLevel.Private ); await _context.StorageFiles.AddAsync(storageFile, cancellationToken); diff --git a/services/storage-service-net/src/StorageService.Infrastructure/Storage/AliyunOssStorageProvider.cs b/services/storage-service-net/src/StorageService.Infrastructure/Storage/AliyunOssStorageProvider.cs index 430d1719..5e7a5171 100644 --- a/services/storage-service-net/src/StorageService.Infrastructure/Storage/AliyunOssStorageProvider.cs +++ b/services/storage-service-net/src/StorageService.Infrastructure/Storage/AliyunOssStorageProvider.cs @@ -3,6 +3,8 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using StorageService.Domain.AggregatesModel.FileAggregate; using StorageService.Infrastructure.Configuration; +using OssPartETag = Aliyun.OSS.PartETag; +using System.Linq; namespace StorageService.Infrastructure.Storage; @@ -171,9 +173,33 @@ public class AliyunOssStorageProvider : IStorageProvider string contentType, CancellationToken cancellationToken = default) { - // TODO: Implement using Aliyun OSS InitiateMultipartUpload - _logger.LogWarning("Multipart upload for Aliyun OSS not yet implemented"); - throw new NotImplementedException("Multipart upload will be implemented in next phase"); + try + { + EnsureBucketExistsAsync(bucketName, cancellationToken).Wait(cancellationToken); + + var request = new InitiateMultipartUploadRequest(bucketName, objectKey) + { + ContentType = contentType + }; + + var response = _ossClient.InitiateMultipartUpload(request); + _logger.LogInformation( + "Initiated Aliyun OSS multipart upload: {Bucket}/{ObjectKey}, UploadId: {UploadId}", + bucketName, + objectKey, + response.UploadId); + + return Task.FromResult(response.UploadId); + } + catch (Exception ex) + { + _logger.LogError( + ex, + "Failed to initiate Aliyun OSS multipart upload: {Bucket}/{ObjectKey}", + bucketName, + objectKey); + throw; + } } /// @@ -185,9 +211,38 @@ public class AliyunOssStorageProvider : IStorageProvider Stream dataStream, CancellationToken cancellationToken = default) { - // TODO: Implement using Aliyun OSS UploadPart - _logger.LogWarning("Upload part for Aliyun OSS not yet implemented"); - throw new NotImplementedException("Upload part will be implemented in next phase"); + try + { + var request = new UploadPartRequest(bucketName, objectKey, uploadId) + { + InputStream = dataStream, + PartSize = dataStream.Length, + PartNumber = partNumber + }; + + var response = _ossClient.UploadPart(request); + var etag = response.PartETag.ETag; + + _logger.LogInformation( + "Uploaded Aliyun OSS multipart part: {Bucket}/{ObjectKey}, UploadId: {UploadId}, Part: {PartNumber}", + bucketName, + objectKey, + uploadId, + partNumber); + + return Task.FromResult(etag); + } + catch (Exception ex) + { + _logger.LogError( + ex, + "Failed to upload Aliyun OSS multipart part: {Bucket}/{ObjectKey}, UploadId: {UploadId}, Part: {PartNumber}", + bucketName, + objectKey, + uploadId, + partNumber); + throw; + } } /// @@ -198,9 +253,35 @@ public class AliyunOssStorageProvider : IStorageProvider List parts, CancellationToken cancellationToken = default) { - // TODO: Implement using Aliyun OSS CompleteMultipartUpload - _logger.LogWarning("Complete multipart upload for Aliyun OSS not yet implemented"); - throw new NotImplementedException("Complete multipart upload will be implemented in next phase"); + try + { + var request = new CompleteMultipartUploadRequest(bucketName, objectKey, uploadId); + var orderedParts = parts + .OrderBy(part => part.PartNumber) + .Select(part => new OssPartETag(part.PartNumber, part.ETag)) + .ToList(); + + request.PartETags.AddRange(orderedParts); + _ossClient.CompleteMultipartUpload(request); + + _logger.LogInformation( + "Completed Aliyun OSS multipart upload: {Bucket}/{ObjectKey}, UploadId: {UploadId}", + bucketName, + objectKey, + uploadId); + } + catch (Exception ex) + { + _logger.LogError( + ex, + "Failed to complete Aliyun OSS multipart upload: {Bucket}/{ObjectKey}, UploadId: {UploadId}", + bucketName, + objectKey, + uploadId); + throw; + } + + return Task.CompletedTask; } /// @@ -210,8 +291,28 @@ public class AliyunOssStorageProvider : IStorageProvider string uploadId, CancellationToken cancellationToken = default) { - // TODO: Implement using Aliyun OSS AbortMultipartUpload - _logger.LogWarning("Abort multipart upload for Aliyun OSS not yet implemented"); - throw new NotImplementedException("Abort multipart upload will be implemented in next phase"); + try + { + var request = new AbortMultipartUploadRequest(bucketName, objectKey, uploadId); + _ossClient.AbortMultipartUpload(request); + + _logger.LogInformation( + "Aborted Aliyun OSS multipart upload: {Bucket}/{ObjectKey}, UploadId: {UploadId}", + bucketName, + objectKey, + uploadId); + } + catch (Exception ex) + { + _logger.LogError( + ex, + "Failed to abort Aliyun OSS multipart upload: {Bucket}/{ObjectKey}, UploadId: {UploadId}", + bucketName, + objectKey, + uploadId); + throw; + } + + return Task.CompletedTask; } }