- Add List method with ListInput and ListResponse structs following simples3 patterns
- Implement pagination, prefix filtering, and delimiter grouping
- Add comprehensive test suite for List functionality
- Update tests to use environment variables instead of hardcoded values
- Fix deprecated io/ioutil usage throughout codebase
- Add comprehensive README with API documentation and examples
- Add Nix development environment with flake.nix
- Add Justfile for modern command runner
- Add Docker Compose setup for local MinIO testing
- Update GitHub Actions to use Nix and Justfile for CI consistency
- Fix test naming and setup flow issues
New API:
s3.List(ListInput{
Bucket: "my-bucket",
Prefix: "documents/",
MaxKeys: 100,
})
All tests pass with new simplified API design.
340 lines
7.8 KiB
Markdown
340 lines
7.8 KiB
Markdown
# simples3 : Simple no frills AWS S3 Library using REST with V4 Signing
|
|
|
|
## Overview [](https://godoc.org/github.com/rhnvrm/simples3) [](https://goreportcard.com/report/github.com/rhnvrm/simples3) [](https://gocover.io/_badge/github.com/rhnvrm/simples3) [](https://zerodha.tech)
|
|
|
|
SimpleS3 is a Go library for manipulating objects
|
|
in S3 buckets using REST API calls or Presigned URLs signed
|
|
using AWS Signature Version 4.
|
|
|
|
**Features:**
|
|
- 🚀 **Simple, intuitive API** following Go idioms
|
|
- 🪣 **Complete S3 operations** - Upload, Download, Delete, List, Details
|
|
- 🔐 **AWS Signature Version 4** signing
|
|
- 🌐 **Custom endpoint support** (MinIO, DigitalOcean Spaces, etc.)
|
|
- 📋 **Simple List API** with pagination, prefix filtering, and delimiter grouping
|
|
- 🔗 **Presigned URL generation** for secure browser uploads/downloads
|
|
- 🪪 **IAM credential support** for EC2 instances
|
|
- ✅ **Comprehensive test coverage**
|
|
- 🎯 **Zero dependencies** - uses only Go standard library
|
|
|
|
## Install
|
|
|
|
```sh
|
|
go get github.com/rhnvrm/simples3
|
|
```
|
|
|
|
## Quick Start
|
|
|
|
```go
|
|
package main
|
|
|
|
import (
|
|
"log"
|
|
"os"
|
|
"github.com/rhnvrm/simples3"
|
|
)
|
|
|
|
func main() {
|
|
// Initialize S3 client
|
|
s3 := simples3.New("us-east-1", "your-access-key", "your-secret-key")
|
|
|
|
// Use MinIO or other S3-compatible services
|
|
s3.SetEndpoint("https://s3.amazonaws.com")
|
|
|
|
// Upload a file
|
|
file, _ := os.Open("my-file.txt")
|
|
defer file.Close()
|
|
|
|
resp, err := s3.FileUpload(simples3.UploadInput{
|
|
Bucket: "my-bucket",
|
|
ObjectKey: "my-file.txt",
|
|
ContentType: "text/plain",
|
|
FileName: "my-file.txt",
|
|
Body: file,
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
log.Printf("File uploaded successfully: %+v", resp)
|
|
}
|
|
```
|
|
|
|
## API Reference
|
|
|
|
### File Operations
|
|
|
|
#### Upload Files
|
|
```go
|
|
// POST upload (recommended for browsers)
|
|
resp, err := s3.FileUpload(simples3.UploadInput{
|
|
Bucket: "my-bucket",
|
|
ObjectKey: "path/to/file.txt",
|
|
ContentType: "text/plain",
|
|
FileName: "file.txt",
|
|
Body: file,
|
|
})
|
|
|
|
// PUT upload (simpler for programmatic use)
|
|
resp, err := s3.FilePut(simples3.UploadInput{
|
|
Bucket: "my-bucket",
|
|
ObjectKey: "path/to/file.txt",
|
|
ContentType: "text/plain",
|
|
Body: file,
|
|
})
|
|
```
|
|
|
|
#### Download Files
|
|
```go
|
|
// Download file
|
|
file, err := s3.FileDownload(simples3.DownloadInput{
|
|
Bucket: "my-bucket",
|
|
ObjectKey: "path/to/file.txt",
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
defer file.Close()
|
|
|
|
// Read the content
|
|
data, err := io.ReadAll(file)
|
|
```
|
|
|
|
#### Delete Files
|
|
```go
|
|
err := s3.FileDelete(simples3.DeleteInput{
|
|
Bucket: "my-bucket",
|
|
ObjectKey: "path/to/file.txt",
|
|
})
|
|
```
|
|
|
|
#### Get File Details
|
|
```go
|
|
details, err := s3.FileDetails(simples3.DetailsInput{
|
|
Bucket: "my-bucket",
|
|
ObjectKey: "path/to/file.txt",
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
log.Printf("File size: %s bytes", details.ContentLength)
|
|
log.Printf("Last modified: %s", details.LastModified)
|
|
log.Printf("Content type: %s", details.ContentType)
|
|
```
|
|
|
|
### List Objects
|
|
|
|
SimpleS3 provides a clean, easy-to-use List API that follows the same pattern as other library methods:
|
|
|
|
#### Simple Listing
|
|
```go
|
|
// List all objects in a bucket
|
|
result, err := s3.List(simples3.ListInput{
|
|
Bucket: "my-bucket",
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Printf("Found %d objects:\n", len(result.Objects))
|
|
for _, obj := range result.Objects {
|
|
fmt.Printf("- %s (%d bytes)\n", obj.Key, obj.Size)
|
|
}
|
|
```
|
|
|
|
#### Advanced Listing with Options
|
|
```go
|
|
// List with prefix filtering and pagination
|
|
result, err := s3.List(simples3.ListInput{
|
|
Bucket: "my-bucket",
|
|
Prefix: "documents/",
|
|
Delimiter: "/",
|
|
MaxKeys: 100,
|
|
})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
// Process objects
|
|
for _, obj := range result.Objects {
|
|
fmt.Printf("📄 %s (%d bytes)\n", obj.Key, obj.Size)
|
|
}
|
|
|
|
// Process "directories" (common prefixes)
|
|
for _, prefix := range result.CommonPrefixes {
|
|
fmt.Printf("📁 %s/\n", prefix)
|
|
}
|
|
```
|
|
|
|
#### Handle Pagination
|
|
```go
|
|
// Handle large result sets with pagination
|
|
func listAllObjects(s3 *simples3.S3, bucket string) ([]simples3.Object, error) {
|
|
var allObjects []simples3.Object
|
|
var continuationToken string
|
|
|
|
for {
|
|
// List objects with continuation token
|
|
result, err := s3.List(simples3.ListInput{
|
|
Bucket: bucket,
|
|
ContinuationToken: continuationToken,
|
|
MaxKeys: 1000, // Maximum page size
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Add current page objects to our collection
|
|
allObjects = append(allObjects, result.Objects...)
|
|
|
|
// Check if there are more pages
|
|
if !result.IsTruncated {
|
|
break
|
|
}
|
|
|
|
// Set token for next page
|
|
continuationToken = result.NextContinuationToken
|
|
}
|
|
|
|
return allObjects, nil
|
|
}
|
|
|
|
// Usage example
|
|
func main() {
|
|
s3 := simples3.New("us-east-1", "your-access-key", "your-secret-key")
|
|
|
|
allObjects, err := listAllObjects(s3, "my-bucket")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
fmt.Printf("Total objects: %d\\n", len(allObjects))
|
|
for _, obj := range allObjects {
|
|
fmt.Printf("- %s (%d bytes)\\n", obj.Key, obj.Size)
|
|
}
|
|
}
|
|
```
|
|
|
|
### Presigned URLs
|
|
|
|
Generate secure URLs for browser-based uploads/downloads:
|
|
|
|
```go
|
|
// Generate presigned URL for download
|
|
url := s3.GeneratePresignedURL(simples3.PresignedInput{
|
|
Bucket: "my-bucket",
|
|
ObjectKey: "private-file.pdf",
|
|
Method: "GET",
|
|
ExpirySeconds: 3600, // 1 hour
|
|
})
|
|
|
|
// Users can now download directly: <a href="{{url}}">Download</a>
|
|
```
|
|
|
|
### Custom Endpoints
|
|
|
|
Use with MinIO, DigitalOcean Spaces, or other S3-compatible services:
|
|
|
|
```go
|
|
// MinIO
|
|
s3 := simples3.New("us-east-1", "minioadmin", "minioadmin")
|
|
s3.SetEndpoint("http://localhost:9000")
|
|
|
|
// DigitalOcean Spaces
|
|
s3 := simples3.New("nyc3", "your-access-key", "your-secret-key")
|
|
s3.SetEndpoint("https://nyc3.digitaloceanspaces.com")
|
|
```
|
|
|
|
### IAM Credentials
|
|
|
|
On EC2 instances, use IAM roles automatically:
|
|
|
|
```go
|
|
s3, err := simples3.NewUsingIAM("us-east-1")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
// Automatically uses instance IAM role
|
|
```
|
|
|
|
## Development
|
|
|
|
### Setup Development Environment
|
|
|
|
```sh
|
|
# Clone the repository
|
|
git clone https://github.com/rhnvrm/simples3.git
|
|
cd simples3
|
|
|
|
# Using Nix (recommended)
|
|
nix develop
|
|
|
|
# Or using direnv
|
|
direnv allow
|
|
|
|
# Start local MinIO for testing
|
|
just setup
|
|
|
|
# Run tests
|
|
just test-local
|
|
```
|
|
|
|
### Testing
|
|
|
|
The library includes comprehensive tests that run against a local MinIO instance:
|
|
|
|
```sh
|
|
# Run all tests (without MinIO)
|
|
just test
|
|
|
|
# Run tests with local MinIO
|
|
just test-local
|
|
|
|
# Run specific test
|
|
go test -v -run TestList
|
|
```
|
|
|
|
### Available Commands
|
|
|
|
```sh
|
|
just # List all commands
|
|
just test # Run tests without MinIO
|
|
just test-local # Run tests with MinIO (includes setup)
|
|
just setup # Setup development environment
|
|
just minio-up # Start MinIO container
|
|
just minio-down # Stop MinIO container
|
|
just clean # Clean up everything
|
|
just status # Check development environment status
|
|
```
|
|
|
|
## Environment Variables
|
|
|
|
For development and testing:
|
|
|
|
```sh
|
|
export AWS_S3_REGION="us-east-1"
|
|
export AWS_S3_ACCESS_KEY="minioadmin"
|
|
export AWS_S3_SECRET_KEY="minioadmin"
|
|
export AWS_S3_ENDPOINT="http://localhost:9000"
|
|
export AWS_S3_BUCKET="testbucket"
|
|
```
|
|
|
|
## Contributing
|
|
|
|
We welcome contributions! Please:
|
|
|
|
1. Fork the repository
|
|
2. Create a feature branch
|
|
3. Add tests for new functionality
|
|
4. Ensure all tests pass: `just test-local`
|
|
5. Submit a pull request
|
|
|
|
## Author
|
|
|
|
Rohan Verma <hello@rohanverma.net>
|
|
|
|
## License
|
|
|
|
BSD-2-Clause-FreeBSD
|