{"id":13468246,"url":"https://github.com/LiveUI/S3","last_synced_at":"2025-03-26T05:30:58.161Z","repository":{"id":63916589,"uuid":"76037749","full_name":"LiveUI/S3","owner":"LiveUI","description":"S3 Client written in Swift","archived":false,"fork":false,"pushed_at":"2022-08-03T23:42:43.000Z","size":425,"stargazers_count":98,"open_issues_count":19,"forks_count":63,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-03-17T23:42:19.901Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/LiveUI.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}},"created_at":"2016-12-09T13:29:20.000Z","updated_at":"2025-02-16T17:04:41.000Z","dependencies_parsed_at":"2023-01-14T13:45:14.243Z","dependency_job_id":null,"html_url":"https://github.com/LiveUI/S3","commit_stats":null,"previous_names":[],"tags_count":30,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiveUI%2FS3","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiveUI%2FS3/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiveUI%2FS3/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/LiveUI%2FS3/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/LiveUI","download_url":"https://codeload.github.com/LiveUI/S3/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":245597201,"owners_count":20641859,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","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":[],"created_at":"2024-07-31T15:01:07.568Z","updated_at":"2025-03-26T05:30:57.819Z","avatar_url":"https://github.com/LiveUI.png","language":"Swift","funding_links":[],"categories":["Swift","Packages","HarmonyOS"],"sub_categories":["Cloud","Windows Manager"],"readme":"[![Build Status](https://travis-ci.com/lluuaapp/S3.svg?branch=tests)](https://travis-ci.com/lluuaapp/S3)\n\n# S3 client for Vapor 3\n\n## Functionality\n\n- [x] Signing headers for any region\n- [x] Listing buckets\n- [x] Create bucket\n- [x] Delete bucket\n- [x] Locate bucket region\n- [x] List objects\n- [x] Upload file\n- [x] Get file\n- [x] Delete file\n- [x] Copy file\n- [x] Move file (copy then delete old one)\n- [x] Object info (HEAD)\n- [ ] Object info (ACL)\n- [x] Parsing error responses\n\n## Usage\n\nUpdate dependencies and targets in Package.swift\n\n```swift\ndependencies: [\n    ...\n    .package(url: \"https://github.com/LiveUI/S3.git\", from: \"3.0.0-RC3.2\"),\n],\ntargets: [\n        .target(name: \"App\", dependencies: [\"Vapor\", \"S3\"]),\n        ...\n]\n```\n\nRun ```vapor update```\n\nRegister S3Client as a service in your configure method\n\n```swift\ntry services.register(s3: S3Signer.Config(...), defaultBucket: \"my-bucket\")\n```\n\nto use a custom Minio server, use this Config/Region:\n\n```\nS3Signer.Config(accessKey: accessKey,\n                secretKey: secretKey,\n                region: Region(name: RegionName.usEast1,\n                               hostName: \"127.0.0.1:9000\",\n                               useTLS: false)\n```\n\nuse S3Client\n\n```swift\nimport S3\n\nlet s3 = try req.makeS3Client() // or req.make(S3Client.self) as? S3\ns3.put(...)\ns3.get(...)\ns3.delete(...)\n```\n\nif you only want to use the signer\n\n```swift\nimport S3Signer\n\nlet s3 = try req.makeS3Signer() // or req.make(S3Signer.self)\ns3.headers(...)\n```\n\n### Available methods\n\n```swift\n/// S3 client Protocol\npublic protocol S3Client: Service {\n    \n    /// Get list of objects\n    func buckets(on: Container) -\u003e EventLoopFuture\u003cBucketsInfo\u003e\n    \n    /// Create a bucket\n    func create(bucket: String, region: Region?, on container: Container) -\u003e EventLoopFuture\u003cVoid\u003e\n    \n    /// Delete a bucket\n    func delete(bucket: String, region: Region?, on container: Container) -\u003e EventLoopFuture\u003cVoid\u003e\n    \n    /// Get bucket location\n    func location(bucket: String, on container: Container) -\u003e EventLoopFuture\u003cRegion\u003e\n    \n    /// Get list of objects\n    func list(bucket: String, region: Region?, on container: Container) -\u003e EventLoopFuture\u003cBucketResults\u003e\n    \n    /// Get list of objects\n    func list(bucket: String, region: Region?, headers: [String: String], on container: Container) -\u003e EventLoopFuture\u003cBucketResults\u003e\n    \n    /// Upload file to S3\n    func put(file: File.Upload, headers: [String: String], on: Container) throws -\u003e EventLoopEventLoopFuture\u003cFile.Response\u003e\n    \n    /// Upload file to S3\n    func put(file url: URL, destination: String, access: AccessControlList, on: Container) -\u003e EventLoopFuture\u003cFile.Response\u003e\n    \n    /// Upload file to S3\n    func put(file url: URL, destination: String, bucket: String?, access: AccessControlList, on: Container) -\u003e EventLoopFuture\u003cFile.Response\u003e\n    \n    /// Upload file to S3\n    func put(file path: String, destination: String, access: AccessControlList, on: Container) -\u003e EventLoopFuture\u003cFile.Response\u003e\n    \n    /// Upload file to S3\n    func put(file path: String, destination: String, bucket: String?, access: AccessControlList, on: Container) -\u003e EventLoopFuture\u003cFile.Response\u003e\n    \n    /// Upload file to S3\n    func put(string: String, destination: String, on: Container) -\u003e EventLoopFuture\u003cFile.Response\u003e\n    \n    /// Upload file to S3\n    func put(string: String, destination: String, access: AccessControlList, on: Container) -\u003e EventLoopFuture\u003cFile.Response\u003e\n    \n    /// Upload file to S3\n    func put(string: String, mime: MediaType, destination: String, on: Container) -\u003e EventLoopFuture\u003cFile.Response\u003e\n    \n    /// Upload file to S3\n    func put(string: String, mime: MediaType, destination: String, access: AccessControlList, on: Container) -\u003e EventLoopFuture\u003cFile.Response\u003e\n    \n    /// Upload file to S3\n    func put(string: String, mime: MediaType, destination: String, bucket: String?, access: AccessControlList, on: Container) -\u003e EventLoopFuture\u003cFile.Response\u003e\n    \n    /// Retrieve file data from S3\n    func get(fileInfo file: LocationConvertible, on container: Container) -\u003e EventLoopFuture\u003cFile.Info\u003e\n    \n    /// Retrieve file data from S3\n    func get(fileInfo file: LocationConvertible, headers: [String: String], on container: Container) -\u003e EventLoopFuture\u003cFile.Info\u003e\n    \n    /// Retrieve file data from S3\n    func get(file: LocationConvertible, on: Container) -\u003e EventLoopFuture\u003cFile.Response\u003e\n    \n    /// Retrieve file data from S3\n    func get(file: LocationConvertible, headers: [String: String], on: Container) -\u003e EventLoopFuture\u003cFile.Response\u003e\n    \n    /// Delete file from S3\n    func delete(file: LocationConvertible, on: Container) -\u003e EventLoopFuture\u003cVoid\u003e\n    \n    /// Delete file from S3\n    func delete(file: LocationConvertible, headers: [String: String], on: Container) -\u003e EventLoopFuture\u003cVoid\u003e\n}\n```\n\n### Example usage\n\n```swift\npublic func routes(_ router: Router) throws {\n    \n    // Get all available buckets\n    router.get(\"buckets\")  { req -\u003e EventLoopFuture\u003cBucketsInfo\u003e in\n        let s3 = try req.makeS3Client()\n        return try s3.buckets(on: req)\n    }\n    \n    // Create new bucket\n    router.put(\"bucket\")  { req -\u003e EventLoopFuture\u003cString\u003e in\n        let s3 = try req.makeS3Client()\n        return try s3.create(bucket: \"api-created-bucket\", region: .euCentral1, on: req).map(to: String.self) {\n            return \":)\"\n            }.catchMap({ (error) -\u003e (String) in\n                if let error = error.s3ErrorMessage() {\n                    return error.message\n                }\n                return \":(\"\n            }\n        )\n    }\n    \n    // Locate bucket (get region)\n    router.get(\"bucket/location\")  { req -\u003e EventLoopFuture\u003cString\u003e in\n        let s3 = try req.makeS3Client()\n        return try s3.location(bucket: \"bucket-name\", on: req).map(to: String.self) { region in\n            return region.hostUrlString()\n        }.catchMap({ (error) -\u003e (String) in\n                if let error = error as? S3.Error {\n                    switch error {\n                    case .errorResponse(_, let error):\n                        return error.message\n                    default:\n                        return \"S3 :(\"\n                    }\n                }\n                return \":(\"\n            }\n        )\n    }\n    // Delete bucket\n    router.delete(\"bucket\")  { req -\u003e EventLoopFuture\u003cString\u003e in\n        let s3 = try req.makeS3Client()\n        return try s3.delete(bucket: \"api-created-bucket\", region: .euCentral1, on: req).map(to: String.self) {\n            return \":)\"\n            }.catchMap({ (error) -\u003e (String) in\n                if let error = error.s3ErrorMessage() {\n                    return error.message\n                }\n                return \":(\"\n                }\n        )\n    }\n    \n    // Get list of objects\n    router.get(\"files\")  { req -\u003e EventLoopFuture\u003cBucketResults\u003e in\n        let s3 = try req.makeS3Client()\n        return try s3.list(bucket: \"booststore\", region: .usEast1, headers: [:], on: req).catchMap({ (error) -\u003e (BucketResults) in\n            if let error = error.s3ErrorMessage() {\n                print(error.message)\n            }\n            throw error\n        })\n    }\n    \n    // Demonstrate work with files\n    router.get(\"files/test\") { req -\u003e EventLoopFuture\u003cString\u003e in\n        let string = \"Content of my example file\"\n        \n        let fileName = \"file-hu.txt\"\n        \n        let s3 = try req.makeS3Client()\n        do {\n            // Upload a file from string\n            return try s3.put(string: string, destination: fileName, access: .publicRead, on: req).flatMap(to: String.self) { putResponse in\n                print(\"PUT response:\")\n                print(putResponse)\n                // Get the content of the newly uploaded file\n                return try s3.get(file: fileName, on: req).flatMap(to: String.self) { getResponse in\n                    print(\"GET response:\")\n                    print(getResponse)\n                    print(String(data: getResponse.data, encoding: .utf8) ?? \"Unknown content!\")\n                    // Get info about the file (HEAD)\n                    return try s3.get(fileInfo: fileName, on: req).flatMap(to: String.self) { infoResponse in\n                        print(\"HEAD/Info response:\")\n                        print(infoResponse)\n                        // Delete the file\n                        return try s3.delete(file: fileName, on: req).map() { response in\n                            print(\"DELETE response:\")\n                            print(response)\n                            let json = try JSONEncoder().encode(infoResponse)\n                            return String(data: json, encoding: .utf8) ?? \"Unknown content!\"\n                            }.catchMap({ error -\u003e (String) in\n                                if let error = error.s3ErrorMessage() {\n                                    return error.message\n                                }\n                                return \":(\"\n                            }\n                        )\n                    }\n                }\n            }\n        } catch {\n            print(error)\n            fatalError()\n        }\n    }\n}\n```\n\n## Support\n\nJoin our [Slack](http://bit.ly/2B0dEyt), channel \u003cb\u003e#help-boost\u003c/b\u003e to ... well, get help :) \n\n## Einstore AppStore\n\nCore package for \u003cb\u003e[Einstore](http://www.einstore.io)\u003c/b\u003e, a completely open source enterprise AppStore written in Swift!\n- Website: http://www.einstore.io\n- Github: https://github.com/Einstore/Einstore\n\n## Other core packages\n\n* [EinstoreCore](https://github.com/Einstore/EinstoreCore/) - AppStore core module\n* [ApiCore](https://github.com/LiveUI/ApiCore/) - API core module with users and team management\n* [MailCore](https://github.com/LiveUI/MailCore/) - Mailing wrapper for multiple mailing services like MailGun, SendGrig or SMTP (coming)\n* [DBCore](https://github.com/LiveUI/DbCore/) - Set of tools for work with PostgreSQL database\n* [VaporTestTools](https://github.com/LiveUI/VaporTestTools) - Test tools and helpers for Vapor 3\n\n## Code contributions\n\nWe love PR’s, we can’t get enough of them ... so if you have an interesting improvement, bug-fix or a new feature please don’t hesitate to get in touch. If you are not sure about something before you start the development you can always contact our dev and product team through our Slack.\n\n## Credits\n\n#### Author\nOndrej Rafaj (@rafiki270 on [Github](https://github.com/rafiki270), [Twitter](https://twitter.com/rafiki270), [LiveUI Slack](http://bit.ly/2B0dEyt) and [Vapor Slack](https://vapor.team/))\n\n#### Thanks\nAnthoni Castelli (@anthonycastelli on [Github](https://github.com/anthonycastelli), @anthony on [Vapor Slack](https://vapor.team/)) for his help on updating S3Signer for Vapor3\n\nJustinM1 (@JustinM1 on [Github](https://github.com/JustinM1)) for his amazing original signer package\n\n## License\n\nSee the LICENSE file for more info.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FLiveUI%2FS3","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FLiveUI%2FS3","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FLiveUI%2FS3/lists"}