simples3

package module
v0.10.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 26, 2025 License: BSD-2-Clause Imports: 23 Imported by: 9

README

simples3 : Simple no frills AWS S3 Library using REST with V4 Signing

Overview GoDoc Go Report Card GoCover 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
  • 🔄 Iterator-based ListAll for memory-efficient large bucket iteration (Go 1.23+)
  • 🔗 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

go get github.com/rhnvrm/simples3

Quick Start

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
// 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
// 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
err := s3.FileDelete(simples3.DeleteInput{
    Bucket:    "my-bucket",
    ObjectKey: "path/to/file.txt",
})
Get File Details
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
// 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
// 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)
}
Iterator-based Listing (Go 1.23+)
// Use the new ListAll method for memory-efficient iteration
s3 := simples3.New("us-east-1", "your-access-key", "your-secret-key")

// Iterate over all objects with automatic pagination and error handling
seq, finish := s3.ListAll(simples3.ListInput{
    Bucket: "my-bucket",
    Prefix: "documents/", // Optional filtering
})

for obj := range seq {
    fmt.Printf("📄 %s (%d bytes)\n", obj.Key, obj.Size)
}

// Check for any errors that occurred during iteration
if err := finish(); err != nil {
    log.Fatalf("Error during iteration: %v", err)
}
Handle Pagination (Legacy)
// 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:

// 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:

// 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:

s3, err := simples3.NewUsingIAM("us-east-1")
if err != nil {
    log.Fatal(err)
}
// Automatically uses instance IAM role

Development

Setup Development Environment
# 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:

# 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
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:

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

Documentation

Index

Constants

View Source
const (

	// AMZMetaPrefix to prefix metadata key.
	AMZMetaPrefix = "x-amz-meta-"
)
View Source
const (
	HdrXAmzSignedHeaders = "X-Amz-SignedHeaders"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type DeleteInput

type DeleteInput struct {
	Bucket    string
	ObjectKey string
}

DeleteInput is passed to FileDelete as a parameter.

type DetailsInput added in v0.7.0

type DetailsInput struct {
	Bucket    string
	ObjectKey string
}

DetailsInput is passed to FileDetails as a parameter.

type DetailsResponse added in v0.7.0

type DetailsResponse struct {
	ContentType   string
	ContentLength string
	AcceptRanges  string
	Date          string
	Etag          string
	LastModified  string
	Server        string
	AmzID2        string
	AmzRequestID  string
	AmzMeta       map[string]string
	ExtraHeaders  map[string]string
}

DetailsResponse is returned by FileDetails.

type DownloadInput added in v0.5.0

type DownloadInput struct {
	Bucket    string
	ObjectKey string
}

DownloadInput is passed to FileDownload as a parameter.

type IAMResponse

type IAMResponse struct {
	Code            string    `json:"Code"`
	LastUpdated     string    `json:"LastUpdated"`
	Type            string    `json:"Type"`
	AccessKeyID     string    `json:"AccessKeyId"`
	SecretAccessKey string    `json:"SecretAccessKey"`
	Token           string    `json:"Token"`
	Expiration      time.Time `json:"Expiration"`
}

IAMResponse is used by NewUsingIAM to auto detect the credentials.

type ListInput added in v0.10.0

type ListInput struct {
	// Required: The name of the bucket to list objects from
	Bucket string

	// Optional: A delimiter to group objects by (commonly "/")
	Delimiter string

	// Optional: Only list objects starting with this prefix
	Prefix string

	// Optional: Maximum number of objects to return
	MaxKeys int64

	// Optional: Token for pagination from a previous request
	ContinuationToken string

	// Optional: Object key to start listing after
	StartAfter string
}

ListInput is passed to List as a parameter.

type ListResponse added in v0.10.0

type ListResponse struct {
	// Name of the bucket
	Name string

	// Whether the results were truncated (more results available)
	IsTruncated bool

	// Token to get the next page of results (if truncated)
	NextContinuationToken string

	// List of objects in the bucket
	Objects []Object

	// Common prefixes when using delimiter (like directories)
	CommonPrefixes []string

	// Total number of keys returned
	KeyCount int64
}

ListResponse is returned by List.

type Object added in v0.10.0

type Object struct {
	// Name of the object
	Key string

	// Size in bytes
	Size int64

	// When the object was last modified
	LastModified string

	// Entity tag of the object
	ETag string

	// Storage class (e.g., "STANDARD")
	StorageClass string
}

Object represents an S3 object in a bucket.

type PolicyJSON

type PolicyJSON struct {
	Expiration string        `json:"expiration"`
	Conditions []interface{} `json:"conditions"`
}

PolicyJSON is policy rule.

type PresignedInput

type PresignedInput struct {
	Bucket                     string
	ObjectKey                  string
	Method                     string
	Timestamp                  time.Time
	ExtraHeaders               map[string]string
	ExpirySeconds              int
	ResponseContentDisposition string
}

PresignedInput is passed to GeneratePresignedURL as a parameter.

type PutResponse added in v0.8.0

type PutResponse struct {
	ETag    string
	Headers http.Header
}

PutResponse is returned when the action is successful, and the service sends back an HTTP 200 response. The response returns ETag along with HTTP headers.

type S3

type S3 struct {
	AccessKey string
	SecretKey string
	Region    string
	Client    *http.Client

	Token     string
	Endpoint  string
	URIFormat string
	// contains filtered or unexported fields
}

S3 provides a wrapper around your S3 credentials.

func New

func New(region, accessKey, secretKey string) *S3

New returns an instance of S3.

func NewUsingIAM

func NewUsingIAM(region string) (*S3, error)

NewUsingIAM automatically generates an Instance of S3 using instance metatdata.

func (*S3) CreateUploadPolicies

func (s3 *S3) CreateUploadPolicies(uploadConfig UploadConfig) (UploadPolicies, error)

CreateUploadPolicies creates amazon s3 sigv4 compatible policy and signing keys with the signature returns the upload policy. https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/API/sigv4-authentication-HTTPPOST.html

func (*S3) FileDelete

func (s3 *S3) FileDelete(u DeleteInput) error

FileDelete makes a DELETE call with the file written as multipart and on successful upload, checks for 204 No Content.

func (*S3) FileDetails added in v0.7.0

func (s3 *S3) FileDetails(u DetailsInput) (DetailsResponse, error)

func (*S3) FileDownload added in v0.5.0

func (s3 *S3) FileDownload(u DownloadInput) (io.ReadCloser, error)

FileDownload makes a GET call and returns a io.ReadCloser. After reading the response body, ensure closing the response.

func (*S3) FilePut added in v0.8.0

func (s3 *S3) FilePut(u UploadInput) (PutResponse, error)

FilePut makes a PUT call to S3.

func (*S3) FileUpload

func (s3 *S3) FileUpload(u UploadInput) (UploadResponse, error)

FileUpload makes a POST call with the file written as multipart and on successful upload, checks for 200 OK.

func (*S3) GeneratePresignedURL

func (s3 *S3) GeneratePresignedURL(in PresignedInput) string

GeneratePresignedURL creates a Presigned URL that can be used for Authentication using Query Parameters. (https://docs.aws.amazon.com/AmazonS3/latest/API/sigv4-query-string-auth.html)

func (*S3) List added in v0.10.0

func (s3 *S3) List(input ListInput) (ListResponse, error)

List implements a simple S3 object listing API

func (*S3) ListAll added in v0.10.0

func (s3 *S3) ListAll(input ListInput) (iter.Seq[Object], func() error)

ListAll returns an iterator that yields all objects in the bucket, automatically handling pagination. It also returns a finish callback that should be called after iteration to check for any errors.

func (*S3) SetClient added in v0.3.0

func (s3 *S3) SetClient(client *http.Client) *S3

SetClient can be used to set the http client to be used by the package. If client passed is nil, http.DefaultClient is used.

func (*S3) SetEndpoint added in v0.5.0

func (s3 *S3) SetEndpoint(uri string) *S3

SetEndpoint can be used to the set a custom endpoint for using an alternate instance compatible with the s3 API. If no protocol is included in the URI, defaults to HTTPS.

func (*S3) SetIAMData added in v0.8.4

func (s3 *S3) SetIAMData(iamResp IAMResponse)

setIAMData sets the IAM data on the S3 instance.

func (*S3) SetToken added in v0.6.1

func (s3 *S3) SetToken(token string) *S3

SetToken can be used to set a Temporary Security Credential token obtained from using an IAM role or AWS STS.

type S3Error added in v0.10.0

type S3Error struct {
	XMLName   xml.Name `xml:"Error"`
	Code      string   `xml:"Code"`
	Message   string   `xml:"Message"`
	RequestID string   `xml:"RequestId"`
	HostID    string   `xml:"HostId"`
}

S3Error represents an S3 API error response

func (S3Error) Error added in v0.10.0

func (e S3Error) Error() string

Error returns a string representation of the S3Error

type UploadConfig

type UploadConfig struct {
	// Required
	BucketName         string
	ObjectKey          string
	ContentType        string
	ContentDisposition string
	ACL                string
	FileSize           int64
	// Optional
	UploadURL  string
	Expiration time.Duration
	MetaData   map[string]string
}

UploadConfig generate policies from config for POST requests to S3 using Signing V4.

type UploadInput

type UploadInput struct {
	// essential fields
	Bucket      string
	ObjectKey   string
	FileName    string
	ContentType string

	// optional fields
	ContentDisposition string
	ACL                string
	// Setting key/value pairs adds user-defined metadata
	// keys to the object, prefixed with AMZMetaPrefix.
	CustomMetadata map[string]string

	Body io.ReadSeeker
}

UploadInput is passed to FileUpload as a parameter.

type UploadPolicies

type UploadPolicies struct {
	URL  string
	Form map[string]string
}

UploadPolicies Amazon s3 upload policies.

type UploadResponse added in v0.2.0

type UploadResponse struct {
	Location string `xml:"Location"`
	Bucket   string `xml:"Bucket"`
	Key      string `xml:"Key"`
	ETag     string `xml:"ETag"`
}

UploadResponse receives the following XML in case of success, since we set a 201 response from S3. Sample response:

<PostResponse>
  <Location>https://s3.amazonaws.com/link-to-the-file</Location>
  <Bucket>s3-bucket</Bucket>
  <Key>development/8614bd40-691b-4668-9241-3b342c6cf429/image.jpg</Key>
  <ETag>"32-bit-tag"</ETag>
</PostResponse>

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL