Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

AWS S3 File Operations

Overview Questions

  • Làm sao để upload file lên AWS S3?
  • Làm sao để download file từ S3?
  • Làm sao để delete file trong S3?
  • Presigned URL là gì và khi nào sử dụng?
  • Cách cấu hình S3 client trong ASP.NET Core?

AWS S3 Configuration

Cài đặt Package

dotnet add package AWSSDK.S3

Cấu hình trong Program.cs

// Program.cs
builder.Services.AddAWSService<IAmazonS3>(new AmazonS3Config
{
    RegionEndpoint = RegionEndpoint.USEast1
});

// Hoặc sử dụng options pattern
builder.Services.Configure<AwsS3Options>(
    builder.Configuration.GetSection("AWS:S3"));

// Cấu hình trong appsettings.json
/*
{
  "AWS": {
    "S3": {
      "BucketName": "my-bucket",
      "Region": "us-east-1"
    }
  }
}
*/

Cấu hình với Credentials

// Sử dụng AWS credentials
var credentials = new BasicAWSCredentials("access-key", "secret-key");

builder.Services.AddAWSService<IAmazonS3>(new AmazonS3Config
{
    RegionEndpoint = RegionEndpoint.USEast1
}, credentials);

// Hoặc sử dụng IAM role (trên EC2, Lambda)
builder.Services.AddAWSService<IAmazonS3>();

Upload Files

Simple Upload

public class S3Service
{
    private readonly IAmazonS3 _s3Client;

    public S3Service(IAmazonS3 s3Client)
    {
        _s3Client = s3Client;
    }

    public async Task<string> UploadFileAsync(Stream fileStream, string key)
    {
        var request = new PutObjectRequest
        {
            BucketName = "my-bucket",
            Key = key,
            InputStream = fileStream,
            ContentType = "application/octet-stream"
        };

        var response = await _s3Client.PutObjectAsync(request);
        
        return $"https://my-bucket.s3.amazonaws.com/{key}";
    }
}

Upload với Metadata

public async Task<string> UploadWithMetadataAsync(
    Stream fileStream, 
    string key, 
    string contentType,
    Dictionary<string, string> metadata)
{
    var request = new PutObjectRequest
    {
        BucketName = "my-bucket",
        Key = key,
        InputStream = fileStream,
        ContentType = contentType
    };

    // Thêm metadata
    foreach (var item in metadata)
    {
        request.Metadata[item.Key] = item.Value;
    }

    await _s3Client.PutObjectAsync(request);
    return key;
}

Upload Large Files (Multipart)

public async Task<string> UploadLargeFileAsync(string filePath, string key)
{
    var fileInfo = new FileInfo(filePath);
    const int partSize = 5 * 1024 * 1024; // 5MB

    var initiateRequest = new CreateMultipartUploadRequest
    {
        BucketName = "my-bucket",
        Key = key,
        ContentType = "application/octet-stream"
    };

    var initiateResponse = await _s3Client.CreateMultipartUploadAsync(initiateRequest);

    var uploadParts = new List<UploadPartResponse>();
    var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
    var buffer = new byte[partSize];
    var partNumber = 1;

    try
    {
        while (fileStream.Position < fileInfo.Length)
        {
            var bytesRead = await fileStream.ReadAsync(buffer, 0, partSize);
            var partRequest = new UploadPartRequest
            {
                BucketName = "my-bucket",
                Key = key,
                UploadId = initiateResponse.UploadId,
                PartNumber = partNumber,
                InputStream = new MemoryStream(buffer, 0, bytesRead),
                PartSize = bytesRead
            };

            var uploadResponse = await _s3Client.UploadPartAsync(partRequest);
            uploadParts.Add(uploadResponse);
            partNumber++;
        }

        var completeRequest = new CompleteMultipartUploadRequest
        {
            BucketName = "my-bucket",
            Key = key,
            UploadId = initiateResponse.UploadId,
            PartETags = uploadParts.Select((p, i) => new PartETag(i + 1, p.ETag)).ToList()
        };

        await _s3Client.CompleteMultipartUploadAsync(completeRequest);
        return key;
    }
    catch (Exception)
    {
        await _s3Client.AbortMultipartUploadAsync(new AbortMultipartUploadRequest
        {
            BucketName = "my-bucket",
            Key = key,
            UploadId = initiateResponse.UploadId
        });
        throw;
    }
    finally
    {
        fileStream.Dispose();
    }
}

Download Files

Download to Stream

public async Task<Stream> DownloadFileAsync(string key)
{
    var request = new GetObjectRequest
    {
        BucketName = "my-bucket",
        Key = key
    };

    var response = await _s3Client.GetObjectAsync(request);
    return response.ResponseStream;
}

Download to Local File

public async Task DownloadToFileAsync(string key, string localPath)
{
    var request = new GetObjectRequest
    {
        BucketName = "my-bucket",
        Key = key
    };

    var response = await _s3Client.GetObjectAsync(request);
    
    using var writer = new FileStream(localPath, FileMode.Create);
    await response.ResponseStream.CopyToAsync(writer);
}

Get File URL

public string GetFileUrl(string key, TimeSpan? expiry = null)
{
    var request = new GetPreSignedUrlRequest
    {
        BucketName = "my-bucket",
        Key = key,
        Expires = DateTime.UtcNow.Add(expiry ?? TimeSpan.FromHours(1))
    };

    return _s3Client.GetPreSignedURL(request);
}

Delete Files

Delete Single File

public async Task DeleteFileAsync(string key)
{
    var request = new DeleteObjectRequest
    {
        BucketName = "my-bucket",
        Key = key
    };

    await _s3Client.DeleteObjectAsync(request);
}

Delete Multiple Files

public async Task DeleteFilesAsync(List<string> keys)
{
    var objects = keys.Select(key => new KeyVersion { Key = key }).ToList();
    
    var request = new DeleteObjectsRequest
    {
        BucketName = "my-bucket",
        Objects = objects
    };

    var response = await _s3Client.DeleteObjectsAsync(request);
}

Delete Folder (Prefix)

public async Task DeleteFolderAsync(string prefix)
{
    // List all objects with the prefix
    var listRequest = new ListObjectsV2Request
    {
        BucketName = "my-bucket",
        Prefix = prefix
    };

    var objectsToDelete = new List<KeyVersion>();

    var response = await _s3Client.ListObjectsV2Async(listRequest);
    
    foreach (var s3Object in response.S3Objects)
    {
        objectsToDelete.Add(new KeyVersion { Key = s3Object.Key });
    }

    if (objectsToDelete.Any())
    {
        var deleteRequest = new DeleteObjectsRequest
        {
            BucketName = "my-bucket",
            Objects = objectsToDelete
        };

        await _s3Client.DeleteObjectsAsync(deleteRequest);
    }
}

List Files

List All Files

public async Task<List<S3Object>> ListFilesAsync(string prefix = "")
{
    var request = new ListObjectsV2Request
    {
        BucketName = "my-bucket",
        Prefix = prefix
    };

    var response = await _s3Client.ListObjectsV2Async(request);
    return response.S3Objects.ToList();
}

List Files with Pagination

public async Task<List<S3Object>> ListAllFilesAsync(string prefix = "")
{
    var files = new List<S3Object>();
    var request = new ListObjectsV2Request
    {
        BucketName = "my-bucket",
        Prefix = prefix
    };

    ListObjectsV2Response response;
    do
    {
        response = await _s3Client.ListObjectsV2Async(request);
        files.AddRange(response.S3Objects);
        request.ContinuationToken = response.NextContinuationToken;
    } while (response.IsTruncated);

    return files;
}

Presigned URLs

Generate Presigned URL

public string GeneratePresignedUrl(string key, TimeSpan expiry)
{
    var request = new GetPreSignedUrlRequest
    {
        BucketName = "my-bucket",
        Key = key,
        Expires = DateTime.UtcNow.Add(expiry)
    };

    return _s3Client.GetPreSignedURL(request);
}

Generate Upload URL (For Direct Browser Upload)

public string GenerateUploadUrl(string key, string contentType, TimeSpan expiry)
{
    var request = new PutObjectRequest
    {
        BucketName = "my-bucket",
        Key = key,
        ContentType = contentType
    };

    // Get presigned URL for upload
    return _s3Client.GetPreSignedURL(request);
}

Best Practices

1. Use Appropriate Storage Class

// Sử dụng S3 Standard cho frequently accessed files
var request = new PutObjectRequest
{
    BucketName = "my-bucket",
    Key = key,
    StorageClass = S3StorageClass.Standard
};

// Sử dụng S3 Standard-IA cho infrequently accessed files
request.StorageClass = S3StorageClass.StandardInfrequentAccess;

// Sử dụng S3 Glacier cho archival files
request.StorageClass = S3StorageClass.Glacier;

2. Enable Encryption

// Server-side encryption với AWS-managed key
var request = new PutObjectRequest
{
    BucketName = "my-bucket",
    Key = key,
    ServerSideEncryptionMethod = ServerSideEncryptionMethod.AES256
};

// Hoặc với KMS key
request.ServerSideEncryptionMethod = ServerSideEncryptionMethod.AWSKMS;
request.ServerSideEncryptionKeyManagementServiceKeyId = kmsKeyId;

3. Set Proper Permissions

// IAM Policy example
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:DeleteObject"
            ],
            "Resource": "arn:aws:s3:::my-bucket/*"
        }
    ]
}

4. Handle Errors Properly

try
{
    await _s3Client.PutObjectAsync(request);
}
catch (AmazonS3Exception ex)
{
    // Handle S3-specific errors
    Console.WriteLine($"S3 Error: {ex.ErrorCode} - {ex.Message}");
    throw;
}
catch (Exception ex)
{
    // Handle general errors
    Console.WriteLine($"Error: {ex.Message}");
    throw;
}