{"id":13414079,"url":"https://github.com/rhnvrm/simples3","last_synced_at":"2026-01-23T09:25:17.217Z","repository":{"id":34153936,"uuid":"160658086","full_name":"rhnvrm/simples3","owner":"rhnvrm","description":"Simple no frills AWS S3 Golang Library using REST with V4 Signing (without AWS Go SDK)","archived":false,"fork":false,"pushed_at":"2025-11-27T12:10:29.000Z","size":167,"stargazers_count":178,"open_issues_count":0,"forks_count":31,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-11-30T02:31:14.607Z","etag":null,"topics":["aws","aws-s3","aws-signature-v4","golang"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rhnvrm.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":"ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2018-12-06T10:24:21.000Z","updated_at":"2025-11-27T12:10:34.000Z","dependencies_parsed_at":"2024-10-25T18:38:34.201Z","dependency_job_id":"5f1df3d6-6afc-4d94-aea9-6f249be6f1df","html_url":"https://github.com/rhnvrm/simples3","commit_stats":null,"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"purl":"pkg:github/rhnvrm/simples3","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rhnvrm%2Fsimples3","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rhnvrm%2Fsimples3/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rhnvrm%2Fsimples3/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rhnvrm%2Fsimples3/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rhnvrm","download_url":"https://codeload.github.com/rhnvrm/simples3/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rhnvrm%2Fsimples3/sbom","scorecard":{"id":774409,"data":{"date":"2025-08-18","repo":{"name":"github.com/rhnvrm/simples3","commit":"59a29ac4b1c082e649bcca579729decc71e940ef"},"scorecard":{"version":"v5.2.1-41-g40576783","commit":"40576783fda6698350fcbbeaea760ff827433034"},"score":4.2,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#binary-artifacts"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/go-test.yml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":3,"reason":"Found 6/17 approved changesets -- score normalized to 3","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#code-review"}},{"name":"Maintained","score":1,"reason":"2 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":0,"reason":"dependency not pinned by hash detected -- score normalized to 0","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go-test.yml:13: update your workflow using https://app.stepsecurity.io/secureworkflow/rhnvrm/simples3/go-test.yml/master?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/go-test.yml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/rhnvrm/simples3/go-test.yml/master?enable=pin","Info:   0 out of   2 GitHub-owned GitHubAction dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#fuzzing"}},{"name":"License","score":9,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Warn: project license file does not contain an FSF or OSI license."],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 26 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/40576783fda6698350fcbbeaea760ff827433034/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-23T03:08:58.516Z","repository_id":34153936,"created_at":"2025-08-23T03:08:58.516Z","updated_at":"2025-08-23T03:08:58.516Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28686228,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-23T05:48:07.525Z","status":"ssl_error","status_checked_at":"2026-01-23T05:48:07.129Z","response_time":59,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","aws-s3","aws-signature-v4","golang"],"created_at":"2024-07-30T20:01:57.043Z","updated_at":"2026-01-23T09:25:17.210Z","avatar_url":"https://github.com/rhnvrm.png","language":"Go","readme":"# simples3 : Simple no frills AWS S3 Library using REST with V4 Signing\n\n## Overview [![GoDoc](https://godoc.org/github.com/rhnvrm/simples3?status.svg)](https://godoc.org/github.com/rhnvrm/simples3) [![Go Report Card](https://goreportcard.com/badge/github.com/rhnvrm/simples3)](https://goreportcard.com/report/github.com/rhnvrm/simples3) [![GoCover](https://gocover.io/_badge/github.com/rhnvrm/simples3)](https://gocover.io/_badge/github.com/rhnvrm/simples3) [![Zerodha Tech](https://zerodha.tech/static/images/github-badge.svg)](https://zerodha.tech)\n\nSimpleS3 is a Go library for manipulating objects\nin S3 buckets using REST API calls or Presigned URLs signed\nusing AWS Signature Version 4.\n\n**Features:**\n- **Simple, intuitive API** following Go idioms\n- **Complete S3 operations** - Upload, Download, Delete, List, Details\n- **Multipart upload** - Large file support with parallel uploads, progress tracking, and resumability\n- **Bucket management** - Create, Delete, and List buckets\n- **AWS Signature Version 4** signing\n- **Custom endpoint support** (MinIO, DigitalOcean Spaces, etc.)\n- **Simple List API** with pagination, prefix filtering, and delimiter grouping\n- **Iterator-based ListAll** for memory-efficient large bucket iteration (Go 1.23+)\n- **Presigned URL generation** for secure browser uploads/downloads (including multipart)\n- **Object Versioning** - Enable versioning, list versions, and access specific object versions\n- **Server-Side Encryption** - Support for SSE-S3 (AES256) and SSE-KMS encryption\n- **IAM credential support** for EC2 instances\n- **Comprehensive test coverage**\n- **Zero dependencies** - uses only Go standard library\n\n## Install\n\n```sh\ngo get github.com/rhnvrm/simples3\n```\n\n## Quick Start\n\n```go\npackage main\n\nimport (\n    \"log\"\n    \"os\"\n    \"github.com/rhnvrm/simples3\"\n)\n\nfunc main() {\n    // Initialize S3 client\n    s3 := simples3.New(\"us-east-1\", \"your-access-key\", \"your-secret-key\")\n\n    // Upload a file\n    file, err := os.Open(\"my-file.txt\")\n    if err != nil {\n        log.Fatal(err)\n    }\n    defer file.Close()\n\n    resp, err := s3.FileUpload(simples3.UploadInput{\n        Bucket:      \"my-bucket\",\n        ObjectKey:   \"my-file.txt\",\n        ContentType: \"text/plain\",\n        FileName:    \"my-file.txt\",\n        Body:        file,\n    })\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    log.Printf(\"File uploaded successfully: %+v\", resp)\n}\n```\n\n## API Reference\n\n### Bucket Operations\n\n#### List All Buckets\n```go\n// List all buckets for the AWS account\nresult, err := s3.ListBuckets(simples3.ListBucketsInput{})\nif err != nil {\n    log.Fatal(err)\n}\n\nfmt.Printf(\"Found %d buckets:\\n\", len(result.Buckets))\nfor _, bucket := range result.Buckets {\n    fmt.Printf(\"- %s (created: %s)\\n\", bucket.Name, bucket.CreationDate)\n}\n\n// Owner information is also available\nfmt.Printf(\"Owner: %s (%s)\\n\", result.Owner.DisplayName, result.Owner.ID)\n```\n\n#### Create a Bucket\n```go\n// Create a new bucket\noutput, err := s3.CreateBucket(simples3.CreateBucketInput{\n    Bucket: \"my-new-bucket\",\n    Region: \"us-east-1\", // Optional, defaults to client region\n})\nif err != nil {\n    log.Fatal(err)\n}\n\nfmt.Printf(\"Bucket created at: %s\\n\", output.Location)\n```\n\n#### Delete a Bucket\n```go\n// Delete an empty bucket\nerr := s3.DeleteBucket(simples3.DeleteBucketInput{\n    Bucket: \"my-old-bucket\",\n})\nif err != nil {\n    log.Fatal(err)\n}\n\n// Note: The bucket must be empty before deletion\n```\n\n### File Operations\n\n#### Upload Files\n```go\n// POST upload (recommended for browsers)\nresp, err := s3.FileUpload(simples3.UploadInput{\n    Bucket:      \"my-bucket\",\n    ObjectKey:   \"path/to/file.txt\",\n    ContentType: \"text/plain\",\n    FileName:    \"file.txt\",\n    Body:        file,\n})\n\n// PUT upload (simpler for programmatic use)\nresp, err := s3.FilePut(simples3.UploadInput{\n    Bucket:      \"my-bucket\",\n    ObjectKey:   \"path/to/file.txt\",\n    ContentType: \"text/plain\",\n    Body:        file,\n})\n```\n\n#### Download Files\n```go\n// Download file\nfile, err := s3.FileDownload(simples3.DownloadInput{\n    Bucket:    \"my-bucket\",\n    ObjectKey: \"path/to/file.txt\",\n})\nif err != nil {\n    log.Fatal(err)\n}\ndefer file.Close()\n\n// Read the content\ndata, err := io.ReadAll(file)\nif err != nil {\n    log.Fatal(err)\n}\n```\n\n#### Delete Files\n```go\nerr := s3.FileDelete(simples3.DeleteInput{\n    Bucket:    \"my-bucket\",\n    ObjectKey: \"path/to/file.txt\",\n})\n```\n\n#### Copy Objects\n```go\n// Copy object within same bucket\noutput, err := s3.CopyObject(simples3.CopyObjectInput{\n    SourceBucket: \"my-bucket\",\n    SourceKey:    \"original/file.txt\",\n    DestBucket:   \"my-bucket\",\n    DestKey:      \"copied/file.txt\",\n})\n\n// Copy across buckets\noutput, err := s3.CopyObject(simples3.CopyObjectInput{\n    SourceBucket: \"source-bucket\",\n    SourceKey:    \"file.txt\",\n    DestBucket:   \"dest-bucket\",\n    DestKey:      \"file.txt\",\n})\n\n// Copy with metadata replacement\noutput, err := s3.CopyObject(simples3.CopyObjectInput{\n    SourceBucket:      \"my-bucket\",\n    SourceKey:         \"file.txt\",\n    DestBucket:        \"my-bucket\",\n    DestKey:           \"file-copy.txt\",\n    MetadataDirective: \"REPLACE\",\n    ContentType:       \"application/json\",\n    CustomMetadata:    map[string]string{\"version\": \"2\"},\n})\n```\n\n#### Batch Delete Files\n```go\n// Delete multiple objects in one request (up to 1000)\noutput, err := s3.DeleteObjects(simples3.DeleteObjectsInput{\n    Bucket:  \"my-bucket\",\n    Objects: []string{\"file1.txt\", \"file2.txt\", \"file3.txt\"},\n    Quiet:   false,\n})\nif err != nil {\n    log.Fatal(err)\n}\n\n// Check results\nfor _, deleted := range output.Deleted {\n    fmt.Printf(\"Deleted: %s\\n\", deleted.Key)\n}\nfor _, errItem := range output.Errors {\n    fmt.Printf(\"Failed to delete %s: %s\\n\", errItem.Key, errItem.Message)\n}\n```\n\n#### Get File Details\n```go\ndetails, err := s3.FileDetails(simples3.DetailsInput{\n    Bucket:    \"my-bucket\",\n    ObjectKey: \"path/to/file.txt\",\n})\nif err != nil {\n    log.Fatal(err)\n}\n\nlog.Printf(\"File size: %s bytes\", details.ContentLength)\nlog.Printf(\"Last modified: %s\", details.LastModified)\nlog.Printf(\"Content type: %s\", details.ContentType)\n```\n\n### List Objects\n\nSimpleS3 provides a clean, easy-to-use List API that follows the same pattern as other library methods:\n\n#### Simple Listing\n```go\n// List all objects in a bucket\nresult, err := s3.List(simples3.ListInput{\n    Bucket: \"my-bucket\",\n})\nif err != nil {\n    log.Fatal(err)\n}\n\nfmt.Printf(\"Found %d objects:\\n\", len(result.Objects))\nfor _, obj := range result.Objects {\n    fmt.Printf(\"- %s (%d bytes)\\n\", obj.Key, obj.Size)\n}\n```\n\n#### Advanced Listing with Options\n```go\n// List with prefix filtering and pagination\nresult, err := s3.List(simples3.ListInput{\n    Bucket:    \"my-bucket\",\n    Prefix:    \"documents/\",\n    Delimiter: \"/\",\n    MaxKeys:   100,\n})\nif err != nil {\n    log.Fatal(err)\n}\n\n// Process objects\nfor _, obj := range result.Objects {\n    fmt.Printf(\"%s (%d bytes)\\n\", obj.Key, obj.Size)\n}\n\n// Process \"directories\" (common prefixes)\nfor _, prefix := range result.CommonPrefixes {\n    fmt.Printf(\"%s/\\n\", prefix)\n}\n```\n\n#### Iterator-based Listing (Go 1.23+)\n```go\n// Use the new ListAll method for memory-efficient iteration\ns3 := simples3.New(\"us-east-1\", \"your-access-key\", \"your-secret-key\")\n\n// Iterate over all objects with automatic pagination and error handling\nseq, finish := s3.ListAll(simples3.ListInput{\n    Bucket: \"my-bucket\",\n    Prefix: \"documents/\", // Optional filtering\n})\n\nfor obj := range seq {\n    fmt.Printf(\"%s (%d bytes)\\n\", obj.Key, obj.Size)\n}\n\n// Check for any errors that occurred during iteration\nif err := finish(); err != nil {\n    log.Fatalf(\"Error during iteration: %v\", err)\n}\n```\n\n#### Handle Pagination (Legacy)\n```go\n// Handle large result sets with pagination\nfunc listAllObjects(s3 *simples3.S3, bucket string) ([]simples3.Object, error) {\n    var allObjects []simples3.Object\n    var continuationToken string\n\n    for {\n        // List objects with continuation token\n        result, err := s3.List(simples3.ListInput{\n            Bucket:            bucket,\n            ContinuationToken: continuationToken,\n            MaxKeys:          1000, // Maximum page size\n        })\n        if err != nil {\n            return nil, err\n        }\n\n        // Add current page objects to our collection\n        allObjects = append(allObjects, result.Objects...)\n\n        // Check if there are more pages\n        if !result.IsTruncated {\n            break\n        }\n\n        // Set token for next page\n        continuationToken = result.NextContinuationToken\n    }\n\n    return allObjects, nil\n}\n\n// Usage example\nfunc main() {\n    s3 := simples3.New(\"us-east-1\", \"your-access-key\", \"your-secret-key\")\n\n    allObjects, err := listAllObjects(s3, \"my-bucket\")\n    if err != nil {\n        log.Fatal(err)\n    }\n\n    fmt.Printf(\"Total objects: %d\\n\", len(allObjects))\n    for _, obj := range allObjects {\n        fmt.Printf(\"- %s (%d bytes)\\n\", obj.Key, obj.Size)\n    }\n}\n```\n\n### Object Tagging\n\nS3 object tags are key-value pairs that you can use to categorize, organize, and manage objects. Each object can have up to 10 tags.\n\n#### Put Tags on an Object\n\nSet or replace all tags on an existing object:\n\n```go\nerr := s3.PutObjectTagging(simples3.PutObjectTaggingInput{\n    Bucket:    \"my-bucket\",\n    ObjectKey: \"my-file.txt\",\n    Tags: map[string]string{\n        \"Environment\": \"production\",\n        \"Project\":     \"website\",\n        \"Version\":     \"v1.2.0\",\n    },\n})\nif err != nil {\n    log.Fatal(err)\n}\n```\n\n#### Get Tags from an Object\n\nRetrieve all tags from an object:\n\n```go\noutput, err := s3.GetObjectTagging(simples3.GetObjectTaggingInput{\n    Bucket:    \"my-bucket\",\n    ObjectKey: \"my-file.txt\",\n})\nif err != nil {\n    log.Fatal(err)\n}\n\nfor key, value := range output.Tags {\n    fmt.Printf(\"%s: %s\\n\", key, value)\n}\n```\n\n#### Delete All Tags from an Object\n\nRemove all tags from an object:\n\n```go\nerr := s3.DeleteObjectTagging(simples3.DeleteObjectTaggingInput{\n    Bucket:    \"my-bucket\",\n    ObjectKey: \"my-file.txt\",\n})\nif err != nil {\n    log.Fatal(err)\n}\n```\n\n#### Upload with Tags\n\nYou can set tags when uploading an object using `FilePut`:\n\n```go\n_, err := s3.FilePut(simples3.UploadInput{\n    Bucket:      \"my-bucket\",\n    ObjectKey:   \"my-file.txt\",\n    ContentType: \"text/plain\",\n    Body:        file,\n    Tags: map[string]string{\n        \"Environment\": \"production\",\n        \"Department\":  \"engineering\",\n    },\n})\n```\n\nNote: Tag support during upload varies by operation:\n- **FilePut** (PUT): ✅ Full support\n- **FileUpload** (POST): ⚠️  AWS S3 supported, MinIO limited (does not support tags in POST policy)\n- **CopyObject**: ✅ Full support (handles MinIO/R2 signature quirks automatically)\n\n### Object Versioning\n\nEnable and manage object versions to protect against accidental deletion or overwrite.\n\n#### Enable Versioning\n\n```go\nerr := s3.PutBucketVersioning(simples3.PutBucketVersioningInput{\n    Bucket: \"my-bucket\",\n    Status: \"Enabled\", // or \"Suspended\"\n})\nif err != nil {\n    log.Fatal(err)\n}\n\n// Check status\nconfig, err := s3.GetBucketVersioning(\"my-bucket\")\nfmt.Printf(\"Versioning status: %s\\n\", config.Status)\n```\n\n#### List Object Versions\n\nList all versions of objects in a bucket:\n\n```go\nresult, err := s3.ListVersions(simples3.ListVersionsInput{\n    Bucket: \"my-bucket\",\n    Prefix: \"important-file.txt\",\n})\nif err != nil {\n    log.Fatal(err)\n}\n\nfor _, version := range result.Versions {\n    fmt.Printf(\"Key: %s, VersionId: %s, IsLatest: %v\\n\",\n        version.Key, version.VersionId, version.IsLatest)\n}\n```\n\n#### Download Specific Version\n\nRetrieve a specific version of an object:\n\n```go\nfile, err := s3.FileDownload(simples3.DownloadInput{\n    Bucket:    \"my-bucket\",\n    ObjectKey: \"my-file.txt\",\n    VersionId: \"v1-version-id\",\n})\nif err != nil {\n    log.Fatal(err)\n}\n// Use file (io.ReadCloser) normally\n```\n\n#### Delete Specific Version\n\nDelete a specific version of an object:\n\n```go\nerr := s3.FileDelete(simples3.DeleteInput{\n    Bucket:    \"my-bucket\",\n    ObjectKey: \"my-file.txt\",\n    VersionId: \"v1-version-id\",\n})\n```\n\n### Server-Side Encryption\n \n Secure your data at rest using Server-Side Encryption (SSE). SimpleS3 supports both SSE-S3 (AES256) and SSE-KMS.\n \n #### Upload with SSE-S3 (AES256)\n \n ```go\n _, err := s3.FilePut(simples3.UploadInput{\n     Bucket:               \"my-bucket\",\n     ObjectKey:            \"secure-file.txt\",\n     Body:                 strings.NewReader(\"secret data\"),\n     ServerSideEncryption: \"AES256\",\n })\n ```\n \n #### Upload with SSE-KMS\n \n ```go\n _, err := s3.FilePut(simples3.UploadInput{\n     Bucket:               \"my-bucket\",\n     ObjectKey:            \"kms-encrypted-file.txt\",\n     Body:                 strings.NewReader(\"secret data\"),\n     ServerSideEncryption: \"aws:kms\",\n     SSEKMSKeyId:          \"arn:aws:kms:us-east-1:123456789012:key/your-key-id\",\n })\n ```\n \n #### Multipart Upload with Encryption\n \n ```go\n output, err := s3.FileUploadMultipart(simples3.MultipartUploadInput{\n     Bucket:               \"my-bucket\",\n     ObjectKey:            \"large-secure-file.mp4\",\n     Body:                 file,\n     ServerSideEncryption: \"AES256\",\n })\n ```\n \n #### Copy with Encryption\n \n ```go\n _, err := s3.CopyObject(simples3.CopyObjectInput{\n     SourceBucket:         \"my-bucket\",\n     SourceKey:            \"original.txt\",\n     DestBucket:           \"my-bucket\",\n     DestKey:              \"encrypted-copy.txt\",\n     ServerSideEncryption: \"AES256\",\n })\n ```\n \n #### Check Encryption Status\n \n ```go\n details, err := s3.FileDetails(simples3.DetailsInput{\n     Bucket:    \"my-bucket\",\n     ObjectKey: \"secure-file.txt\",\n })\n \n fmt.Printf(\"Encryption: %s\\n\", details.ServerSideEncryption)\n if details.SSEKMSKeyId != \"\" {\n     fmt.Printf(\"KMS Key ID: %s\\n\", details.SSEKMSKeyId)\n }\n ```\n \n ### Multipart Upload\n\nFor large files (\u003e100MB), use multipart upload for better performance, resumability, and parallel uploads.\n\n#### High-Level API (Recommended)\n\nThe easiest way to upload large files:\n\n```go\nfile, err := os.Open(\"large-video.mp4\")\nif err != nil {\n    log.Fatal(err)\n}\ndefer file.Close()\n\n// FileUploadMultipart automatically handles chunking and parallel uploads\noutput, err := s3.FileUploadMultipart(simples3.MultipartUploadInput{\n    Bucket:      \"my-bucket\",\n    ObjectKey:   \"videos/large-video.mp4\",\n    Body:        file,\n    ContentType: \"video/mp4\",\n    PartSize:    10 * 1024 * 1024, // 10MB parts (optional, default 5MB)\n    Concurrency: 5,                 // Upload 5 parts in parallel (optional, default 1)\n    OnProgress: func(info simples3.ProgressInfo) {\n        fmt.Printf(\"\\rProgress: %.1f%% (%d/%d parts)\",\n            float64(info.UploadedBytes)/float64(info.TotalBytes)*100,\n            info.CurrentPart, info.TotalParts)\n    },\n})\nif err != nil {\n    log.Fatal(err)\n}\n\nfmt.Printf(\"\\nUploaded: %s (ETag: %s)\\n\", output.Key, output.ETag)\n```\n\n#### Low-Level API (Advanced)\n\nFor more control over the upload process:\n\n```go\n// 1. Initiate multipart upload\ninitOutput, err := s3.InitiateMultipartUpload(simples3.InitiateMultipartUploadInput{\n    Bucket:      \"my-bucket\",\n    ObjectKey:   \"large-file.bin\",\n    ContentType: \"application/octet-stream\",\n})\nif err != nil {\n    log.Fatal(err)\n}\n\n// 2. Upload parts (can be done in parallel or resumed later)\nvar completedParts []simples3.CompletedPart\npartSize := int64(10 * 1024 * 1024) // 10MB\n\nfor partNum := 1; partNum \u003c= totalParts; partNum++ {\n    // Read part data\n    partData := getPartData(partNum, partSize) // Your function to get part data\n\n    output, err := s3.UploadPart(simples3.UploadPartInput{\n        Bucket:     \"my-bucket\",\n        ObjectKey:  \"large-file.bin\",\n        UploadID:   initOutput.UploadID,\n        PartNumber: partNum,\n        Body:       bytes.NewReader(partData),\n        Size:       int64(len(partData)),\n    })\n    if err != nil {\n        // Abort on error\n        s3.AbortMultipartUpload(simples3.AbortMultipartUploadInput{\n            Bucket:    \"my-bucket\",\n            ObjectKey: \"large-file.bin\",\n            UploadID:  initOutput.UploadID,\n        })\n        log.Fatal(err)\n    }\n\n    completedParts = append(completedParts, simples3.CompletedPart{\n        PartNumber: output.PartNumber,\n        ETag:       output.ETag,\n    })\n}\n\n// 3. Complete the upload\nresult, err := s3.CompleteMultipartUpload(simples3.CompleteMultipartUploadInput{\n    Bucket:    \"my-bucket\",\n    ObjectKey: \"large-file.bin\",\n    UploadID:  initOutput.UploadID,\n    Parts:     completedParts,\n})\nif err != nil {\n    log.Fatal(err)\n}\n\nfmt.Printf(\"Upload completed: %s\\n\", result.ETag)\n```\n\n#### List Uploaded Parts\n\nQuery which parts have been uploaded (useful for resuming uploads):\n\n```go\noutput, err := s3.ListParts(simples3.ListPartsInput{\n    Bucket:    \"my-bucket\",\n    ObjectKey: \"large-file.bin\",\n    UploadID:  uploadID,\n})\nif err != nil {\n    log.Fatal(err)\n}\n\nfor _, part := range output.Parts {\n    fmt.Printf(\"Part %d: %d bytes (ETag: %s)\\n\",\n        part.PartNumber, part.Size, part.ETag)\n}\n```\n\n#### Abort Multipart Upload\n\nCancel an in-progress upload and clean up parts:\n\n```go\nerr := s3.AbortMultipartUpload(simples3.AbortMultipartUploadInput{\n    Bucket:    \"my-bucket\",\n    ObjectKey: \"large-file.bin\",\n    UploadID:  uploadID,\n})\n```\n\n#### Browser-Based Multipart Upload\n\nGenerate presigned URLs for each part to enable direct browser uploads:\n\n```go\n// Backend: Initiate and generate presigned URLs\ninitOutput, _ := s3.InitiateMultipartUpload(simples3.InitiateMultipartUploadInput{\n    Bucket:    \"my-bucket\",\n    ObjectKey: \"client-upload.bin\",\n})\n\n// Generate presigned URL for part 1\npresignedURL := s3.GeneratePresignedUploadPartURL(simples3.PresignedMultipartInput{\n    Bucket:        \"my-bucket\",\n    ObjectKey:     \"client-upload.bin\",\n    UploadID:      initOutput.UploadID,\n    PartNumber:    1,\n    ExpirySeconds: 3600,\n})\n\n// Frontend: Upload directly to S3 using the presigned URL (via PUT request)\n// Then send ETags back to backend for completion\n```\n\n### Presigned URLs\n\nGenerate secure URLs for browser-based uploads/downloads:\n\n```go\n// Generate presigned URL for download\nurl := s3.GeneratePresignedURL(simples3.PresignedInput{\n    Bucket:        \"my-bucket\",\n    ObjectKey:     \"private-file.pdf\",\n    Method:        \"GET\",\n    ExpirySeconds: 3600, // 1 hour\n})\n\n// Users can now download directly: \u003ca href=\"{{url}}\"\u003eDownload\u003c/a\u003e\n```\n\n### Custom Endpoints\n\nUse with MinIO, DigitalOcean Spaces, or other S3-compatible services:\n\n```go\n// MinIO\ns3 := simples3.New(\"us-east-1\", \"minioadmin\", \"minioadmin\")\ns3.SetEndpoint(\"http://localhost:9000\")\n\n// DigitalOcean Spaces\ns3 := simples3.New(\"nyc3\", \"your-access-key\", \"your-secret-key\")\ns3.SetEndpoint(\"https://nyc3.digitaloceanspaces.com\")\n```\n\n### IAM Credentials\n\nOn EC2 instances, use IAM roles automatically:\n\n```go\ns3, err := simples3.NewUsingIAM(\"us-east-1\")\nif err != nil {\n    log.Fatal(err)\n}\n// Automatically uses instance IAM role\n```\n\n## Development\n\n### Setup Development Environment\n\n```sh\n# Clone the repository\ngit clone https://github.com/rhnvrm/simples3.git\ncd simples3\n\n# Using Nix (recommended)\nnix develop\n\n# Or using direnv\ndirenv allow\n\n# Start local MinIO for testing\njust setup\n\n# Run tests\njust test-local\n```\n\n### Testing\n\nThe library includes comprehensive tests that run against a local MinIO instance:\n\n```sh\n# Run all tests (without MinIO)\njust test\n\n# Run tests with local MinIO\njust test-local\n\n# Run specific test\ngo test -v -run TestList\n```\n\n### Available Commands\n\n```sh\njust              # List all commands\njust test         # Run tests without MinIO\njust test-local   # Run tests with MinIO (includes setup)\njust setup        # Setup development environment\njust minio-up      # Start MinIO container\njust minio-down    # Stop MinIO container\njust clean         # Clean up everything\njust status        # Check development environment status\n```\n\n## Environment Variables\n\nFor development and testing:\n\n```sh\nexport AWS_S3_REGION=\"us-east-1\"\nexport AWS_S3_ACCESS_KEY=\"minioadmin\"\nexport AWS_S3_SECRET_KEY=\"minioadmin\"\nexport AWS_S3_ENDPOINT=\"http://localhost:9000\"\nexport AWS_S3_BUCKET=\"testbucket\"\n```\n\n## Contributing\n\nContributions welcome! Check [ROADMAP.md](ROADMAP.md) for planned features. Please add tests and ensure `just test-local` passes before submitting PRs.\n\n## Author\n\nRohan Verma \u003chello@rohanverma.net\u003e\n\n## License\n\nBSD-2-Clause-FreeBSD\n","funding_links":[],"categories":["Utility","第三方api","Third-party APIs","第三方API`第三方API 汇总`","Go","第三方API"],"sub_categories":["实用程序/Miscellaneous","Utility/Miscellaneous","HTTP Clients","Fail injection","查询语"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frhnvrm%2Fsimples3","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frhnvrm%2Fsimples3","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frhnvrm%2Fsimples3/lists"}