feat: add CopyObject and DeleteObjects operations

Implements object manipulation capabilities for v0.11.0:
- CopyObject: server-side copy within/across buckets with metadata control
- DeleteObjects: batch delete up to 1000 objects in single request

Both operations follow AWS S3 API specification with proper:
- AWS Signature V4 signing
- XML request/response handling
- Content-MD5 and SHA256 headers
- Comprehensive input validation

Fixed DeleteObjects signature mismatch with MinIO by correcting the canonical
query string format for empty query parameter values (delete= instead of delete).
The writeQuery function now properly appends = to keys with empty values per
AWS Signature V4 specification.

CopyObject enables CLI mv commands. DeleteObjects enables efficient
batch operations for CLI sync/rm.

Tests: All CopyObject and DeleteObjects tests passing with MinIO.

"lets work on the object level operations"
This commit is contained in:
Rohan Verma
2025-11-27 01:18:57 +05:30
parent e410cc846e
commit 9a14996a0b
5 changed files with 559 additions and 23 deletions
+51
View File
@@ -159,6 +159,57 @@ err := s3.FileDelete(simples3.DeleteInput{
})
```
#### Copy Objects
```go
// Copy object within same bucket
output, err := s3.CopyObject(simples3.CopyObjectInput{
SourceBucket: "my-bucket",
SourceKey: "original/file.txt",
DestBucket: "my-bucket",
DestKey: "copied/file.txt",
})
// Copy across buckets
output, err := s3.CopyObject(simples3.CopyObjectInput{
SourceBucket: "source-bucket",
SourceKey: "file.txt",
DestBucket: "dest-bucket",
DestKey: "file.txt",
})
// Copy with metadata replacement
output, err := s3.CopyObject(simples3.CopyObjectInput{
SourceBucket: "my-bucket",
SourceKey: "file.txt",
DestBucket: "my-bucket",
DestKey: "file-copy.txt",
MetadataDirective: "REPLACE",
ContentType: "application/json",
CustomMetadata: map[string]string{"version": "2"},
})
```
#### Batch Delete Files
```go
// Delete multiple objects in one request (up to 1000)
output, err := s3.DeleteObjects(simples3.DeleteObjectsInput{
Bucket: "my-bucket",
Objects: []string{"file1.txt", "file2.txt", "file3.txt"},
Quiet: false,
})
if err != nil {
log.Fatal(err)
}
// Check results
for _, deleted := range output.Deleted {
fmt.Printf("Deleted: %s\n", deleted.Key)
}
for _, errItem := range output.Errors {
fmt.Printf("Failed to delete %s: %s\n", errItem.Key, errItem.Message)
}
```
#### Get File Details
```go
details, err := s3.FileDetails(simples3.DetailsInput{