GithubHelp home page GithubHelp logo

s3's Introduction

Build Status

S3 client for Vapor 3

Functionality

  • Signing headers for any region
  • Listing buckets
  • Create bucket
  • Delete bucket
  • Locate bucket region
  • List objects
  • Upload file
  • Get file
  • Delete file
  • Copy file
  • Move file (copy then delete old one)
  • Object info (HEAD)
  • Object info (ACL)
  • Parsing error responses

Usage

Update dependencies and targets in Package.swift

dependencies: [
    ...
    .package(url: "https://github.com/LiveUI/S3.git", from: "3.0.0-RC3.2"),
],
targets: [
        .target(name: "App", dependencies: ["Vapor", "S3"]),
        ...
]

Run vapor update

Register S3Client as a service in your configure method

try services.register(s3: S3Signer.Config(...), defaultBucket: "my-bucket")

to use a custom Minio server, use this Config/Region:

S3Signer.Config(accessKey: accessKey,
                secretKey: secretKey,
                region: Region(name: RegionName.usEast1,
                               hostName: "127.0.0.1:9000",
                               useTLS: false)

use S3Client

import S3

let s3 = try req.makeS3Client() // or req.make(S3Client.self) as? S3
s3.put(...)
s3.get(...)
s3.delete(...)

if you only want to use the signer

import S3Signer

let s3 = try req.makeS3Signer() // or req.make(S3Signer.self)
s3.headers(...)

Available methods

/// S3 client Protocol
public protocol S3Client: Service {
    
    /// Get list of objects
    func buckets(on: Container) -> EventLoopFuture<BucketsInfo>
    
    /// Create a bucket
    func create(bucket: String, region: Region?, on container: Container) -> EventLoopFuture<Void>
    
    /// Delete a bucket
    func delete(bucket: String, region: Region?, on container: Container) -> EventLoopFuture<Void>
    
    /// Get bucket location
    func location(bucket: String, on container: Container) -> EventLoopFuture<Region>
    
    /// Get list of objects
    func list(bucket: String, region: Region?, on container: Container) -> EventLoopFuture<BucketResults>
    
    /// Get list of objects
    func list(bucket: String, region: Region?, headers: [String: String], on container: Container) -> EventLoopFuture<BucketResults>
    
    /// Upload file to S3
    func put(file: File.Upload, headers: [String: String], on: Container) throws -> EventLoopEventLoopFuture<File.Response>
    
    /// Upload file to S3
    func put(file url: URL, destination: String, access: AccessControlList, on: Container) -> EventLoopFuture<File.Response>
    
    /// Upload file to S3
    func put(file url: URL, destination: String, bucket: String?, access: AccessControlList, on: Container) -> EventLoopFuture<File.Response>
    
    /// Upload file to S3
    func put(file path: String, destination: String, access: AccessControlList, on: Container) -> EventLoopFuture<File.Response>
    
    /// Upload file to S3
    func put(file path: String, destination: String, bucket: String?, access: AccessControlList, on: Container) -> EventLoopFuture<File.Response>
    
    /// Upload file to S3
    func put(string: String, destination: String, on: Container) -> EventLoopFuture<File.Response>
    
    /// Upload file to S3
    func put(string: String, destination: String, access: AccessControlList, on: Container) -> EventLoopFuture<File.Response>
    
    /// Upload file to S3
    func put(string: String, mime: MediaType, destination: String, on: Container) -> EventLoopFuture<File.Response>
    
    /// Upload file to S3
    func put(string: String, mime: MediaType, destination: String, access: AccessControlList, on: Container) -> EventLoopFuture<File.Response>
    
    /// Upload file to S3
    func put(string: String, mime: MediaType, destination: String, bucket: String?, access: AccessControlList, on: Container) -> EventLoopFuture<File.Response>
    
    /// Retrieve file data from S3
    func get(fileInfo file: LocationConvertible, on container: Container) -> EventLoopFuture<File.Info>
    
    /// Retrieve file data from S3
    func get(fileInfo file: LocationConvertible, headers: [String: String], on container: Container) -> EventLoopFuture<File.Info>
    
    /// Retrieve file data from S3
    func get(file: LocationConvertible, on: Container) -> EventLoopFuture<File.Response>
    
    /// Retrieve file data from S3
    func get(file: LocationConvertible, headers: [String: String], on: Container) -> EventLoopFuture<File.Response>
    
    /// Delete file from S3
    func delete(file: LocationConvertible, on: Container) -> EventLoopFuture<Void>
    
    /// Delete file from S3
    func delete(file: LocationConvertible, headers: [String: String], on: Container) -> EventLoopFuture<Void>
}

Example usage

public func routes(_ router: Router) throws {
    
    // Get all available buckets
    router.get("buckets")  { req -> EventLoopFuture<BucketsInfo> in
        let s3 = try req.makeS3Client()
        return try s3.buckets(on: req)
    }
    
    // Create new bucket
    router.put("bucket")  { req -> EventLoopFuture<String> in
        let s3 = try req.makeS3Client()
        return try s3.create(bucket: "api-created-bucket", region: .euCentral1, on: req).map(to: String.self) {
            return ":)"
            }.catchMap({ (error) -> (String) in
                if let error = error.s3ErrorMessage() {
                    return error.message
                }
                return ":("
            }
        )
    }
    
    // Locate bucket (get region)
    router.get("bucket/location")  { req -> EventLoopFuture<String> in
        let s3 = try req.makeS3Client()
        return try s3.location(bucket: "bucket-name", on: req).map(to: String.self) { region in
            return region.hostUrlString()
        }.catchMap({ (error) -> (String) in
                if let error = error as? S3.Error {
                    switch error {
                    case .errorResponse(_, let error):
                        return error.message
                    default:
                        return "S3 :("
                    }
                }
                return ":("
            }
        )
    }
    // Delete bucket
    router.delete("bucket")  { req -> EventLoopFuture<String> in
        let s3 = try req.makeS3Client()
        return try s3.delete(bucket: "api-created-bucket", region: .euCentral1, on: req).map(to: String.self) {
            return ":)"
            }.catchMap({ (error) -> (String) in
                if let error = error.s3ErrorMessage() {
                    return error.message
                }
                return ":("
                }
        )
    }
    
    // Get list of objects
    router.get("files")  { req -> EventLoopFuture<BucketResults> in
        let s3 = try req.makeS3Client()
        return try s3.list(bucket: "booststore", region: .usEast1, headers: [:], on: req).catchMap({ (error) -> (BucketResults) in
            if let error = error.s3ErrorMessage() {
                print(error.message)
            }
            throw error
        })
    }
    
    // Demonstrate work with files
    router.get("files/test") { req -> EventLoopFuture<String> in
        let string = "Content of my example file"
        
        let fileName = "file-hu.txt"
        
        let s3 = try req.makeS3Client()
        do {
            // Upload a file from string
            return try s3.put(string: string, destination: fileName, access: .publicRead, on: req).flatMap(to: String.self) { putResponse in
                print("PUT response:")
                print(putResponse)
                // Get the content of the newly uploaded file
                return try s3.get(file: fileName, on: req).flatMap(to: String.self) { getResponse in
                    print("GET response:")
                    print(getResponse)
                    print(String(data: getResponse.data, encoding: .utf8) ?? "Unknown content!")
                    // Get info about the file (HEAD)
                    return try s3.get(fileInfo: fileName, on: req).flatMap(to: String.self) { infoResponse in
                        print("HEAD/Info response:")
                        print(infoResponse)
                        // Delete the file
                        return try s3.delete(file: fileName, on: req).map() { response in
                            print("DELETE response:")
                            print(response)
                            let json = try JSONEncoder().encode(infoResponse)
                            return String(data: json, encoding: .utf8) ?? "Unknown content!"
                            }.catchMap({ error -> (String) in
                                if let error = error.s3ErrorMessage() {
                                    return error.message
                                }
                                return ":("
                            }
                        )
                    }
                }
            }
        } catch {
            print(error)
            fatalError()
        }
    }
}

Support

Join our Slack, channel #help-boost to ... well, get help :)

Einstore AppStore

Core package for Einstore, a completely open source enterprise AppStore written in Swift!

Other core packages

  • EinstoreCore - AppStore core module
  • ApiCore - API core module with users and team management
  • MailCore - Mailing wrapper for multiple mailing services like MailGun, SendGrig or SMTP (coming)
  • DBCore - Set of tools for work with PostgreSQL database
  • VaporTestTools - Test tools and helpers for Vapor 3

Code contributions

We 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.

Credits

Author

Ondrej Rafaj (@rafiki270 on Github, Twitter, LiveUI Slack and Vapor Slack)

Thanks

Anthoni Castelli (@anthonycastelli on Github, @anthony on Vapor Slack) for his help on updating S3Signer for Vapor3

JustinM1 (@JustinM1 on Github) for his amazing original signer package

License

See the LICENSE file for more info.

s3's People

Contributors

131e55 avatar calebkleveter avatar cellane avatar dpgao avatar fpillet avatar justinm1 avatar ksmandersen avatar lluuaapp avatar macdevnet avatar mliberman avatar mrlotu avatar orafaj-ford avatar rafiki270 avatar vzsg avatar yasumoto avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

s3's Issues

Version tag collision

.package(url: "https://github.com/LiveUI/S3.git", from: "3.0.0-RC3.2"),
resolves to:

      {
        "package": "S3",
        "repositoryURL": "https://github.com/LiveUI/S3.git",
        "state": {
          "branch": null,
          "revision": "9a7e7aa5486396665a792eed6489499ef33c344b",
          "version": "3.0.0-rc2"
        }
      }

Listing an Empty Bucket causes an Error

It seems that is you try call an S3 list on an empty bucket to get an error rather than an empty result set.

[ ERROR ] DecodingError.keyNotFound: Value required for key 'Contents'. (ErrorMiddleware.swift:26)

'File' is not a member type of 'S3'

Using type 'File.Response' generates error 'File' is ambiguous for type lookup in this context. Qualifying it with S3, 'S3.File.Response', generates error 'File' is not a member type of 'S3'.

For example, the response returned in the closure in the code below returns the following error:

expression produced error: error: /var/folders/_p/_xk10nks06103l9_0lsly4c40000gn/T/expr1-8c0981..swift:1:68: error: 'File' is not a member type of 'S3'
Swift._DebuggerSupport.stringForPrintObject(Swift.UnsafePointer<S3.File.Response>(bitPattern: 0x1064fc200)!.pointee)

public func save(file data: Data, to path: String, mime: MediaType, on container: Container) throws -> EventLoopFuture<Void> { let file = File.Upload.init(data: data, destination: path, access: .publicRead, mime: mime.description) return try s3.put(file: file, on: container).map(to: Void.self) { response in return Void() }.catchMap({ error in throw Error.failedWriting(path, error) }) }

It seems like 'put(file:, on:)' is not returning the expected type of Future<File.Response>.

s3.location returns buggy Region for us-west-1

When I call s3.location() for a bucket in region us-west-1, the Region returned looks like this:

Region(name: s3-us-west-1, hostName: nil, useTLS: true)

Then, when calling s3.list() using that location, I get this error:

2019-10-07 15:32:10.520061-0700 Run[70472:4524045] TIC SSL Trust Error [2:0x102cc4bb0]: 3:0 2019-10-07 15:32:10.527247-0700 Run[70472:4524045] NSURLSession/NSURLConnection HTTP load failed (kCFStreamErrorDomainSSL, -9843) 2019-10-07 15:32:10.527305-0700 Run[70472:4524045] Task <5F5D6D8B-0F73-4900-BB34-754DFBD7D862>.<2> HTTP load failed (error code: -1202 [3:-9843]) 2019-10-07 15:32:10.527380-0700 Run[70472:4524047] Task <5F5D6D8B-0F73-4900-BB34-754DFBD7D862>.<2> finished with error - code: -1202 ERROR: Error Domain=NSURLErrorDomain Code=-1202 "The certificate for this server is invalid. You might be connecting to a server that is pretending to be “MY_BUCKET.s3.s3-us-west-1.amazonaws.com” which could put your confidential information at risk." UserInfo={NSURLErrorFailingURLPeerTrustErrorKey=<SecTrustRef: 0x102ccc510>, NSLocalizedRecoverySuggestion=Would you like to connect to the server anyway?, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9843, NSErrorPeerCertificateChainKey=( "<cert(0x103840000) s: *.s3-us-west-1.amazonaws.com i: DigiCert Baltimore CA-2 G2>", "<cert(0x103841c00) s: DigiCert Baltimore CA-2 G2 i: Baltimore CyberTrust Root>" ), NSUnderlyingError=0x102e1fa90 {Error Domain=kCFErrorDomainCFNetwork Code=-1202 "(null)" UserInfo={_kCFStreamPropertySSLClientCertificateState=0, kCFStreamPropertySSLPeerTrust=<SecTrustRef: 0x102ccc510>, _kCFNetworkCFStreamSSLErrorOriginalValue=-9843, _kCFStreamErrorDomainKey=3, _kCFStreamErrorCodeKey=-9843, kCFStreamPropertySSLPeerCertificates=( "<cert(0x103840000) s: *.s3-us-west-1.amazonaws.com i: DigiCert Baltimore CA-2 G2>", "<cert(0x103841c00) s: DigiCert Baltimore CA-2 G2 i: Baltimore CyberTrust Root>" )}}, NSLocalizedDescription=The certificate for this server is invalid. You might be connecting to a server that is pretending to be “MY_BUCKET.s3.s3-us-west-1.amazonaws.com” which could put your confidential information at risk., NSErrorFailingURLKey=https://MY_BUCKET.s3.s3-us-west-1.amazonaws.com/?list-type=2, NSErrorFailingURLStringKey=https://MY_BUCKET.s3.s3-us-west-1.amazonaws.com/?list-type=2, NSErrorClientCertificateStateKey=0} [ ERROR ] Abort.400: The certificate for this server is invalid. You might be connecting to a server that is pretending to be “MY_BUCKET.s3.s3-us-west-1.amazonaws.com” which could put your confidential information at risk. (S3Controller.swift:86)

It looks like the region is actually supposed to be named us-west-1, not s3-us-west-1. If I call s3.list() and pass the .usWest1 region, the call works fine.

Vapor 2.0 Tag

Currently the master branch has the correct Package.swift setup to support Vapor 2.0, but there is no tag available rendering that feature not available for those using SPM. Please tag a new version at master so that the project can be used with Vapor 2.0. Much thanks in advance!

Dependencies still appear to be off

building with 3.0.0-RC3.0.1

backgroundExecute(`
    code: 1,
    error: "error: the package PackageReference(identity: \"s3\", name: nil, path: \"https://github.com/LiveUI/S3.git\", isLocal: false) @ 3.0.0-alpha.1 contains revisioned dependencies:\n    PackageReference(identity: \"vaportesttools\", name: nil, path: \"https://github.com/LiveUI/VaporTestTools.git\", isLocal: false) @ master\n"

Wrong service configuration in readme

As in topic the way how to register S3 service you wrote in readme not works, the solution that worked to me was:

let yourS3Config = S3Signer.Config(accessKey: <yourAccesKey>, secretKey: <yourSecretKey>, region: <yourRegion>)
 try services.register(S3Signer(yourS3Config))

Files being referenced by URL are not working Linux Issue

Hi! Thanks for the awesome project! So I had this issue where everything worked great on Mac but then when I deployed to a Linux environment I had issues. Uploads were happening but the Data sets were zero. So I investgated and found that this line may be the potential culprit:

https://github.com/LiveUI/S3/blob/master/Sources/S3/Extensions/S3%2BPut.swift#L50
in particular let data: Data = try Data(contentsOf: url)

I checked on swift-corelibs-foundation and found that that was fully implemented so it lead me to wonder if there is something different in the way in which Data is poplulated between the two libraries and what I found was a bit interesting.

I switched my code to do the following:

try req.client().get(url).flatMap(to: File.Response.self) { resp in
            guard let data = resp.http.body.data else {
                throw VGSMError(identifier: "unable to access user image data",
                                reason: "Failure in request mostlikely the readson",
                                source:.capture())
            }
            let file = File.Upload(data: data, destination: destination, access: .publicRead)
            return try req.makeS3Client().put(file: file, on: req)
            }

It was the following.

        return try s3Client.put(file: URL(string: url)!,
                     destination: "image.txt",
                     access: .publicRead,
                     on: req)
                     .map { resp in
                        print("Resp \(resp)")
                        return user
        }

    }

After the switch my code worked just fine.

@rafiki270 you may want to consider using the container Client to request the data here if you wanted I could put a PR together that would change this functionality.

'HTTPURLResponse' is unavailable

remote: /app/.build/checkouts/S3/Sources/S3/Extensions/S3+Private.swift:15:107: error: use of undeclared type 'URLRequest'
remote: func make(request url: URL, method: HTTPMethod, headers: HTTPHeaders, data: Data? = nil, cachePolicy: URLRequest.CachePolicy = .useProtocolCachePolicy, on container: Container) throws -> Future {
remote: ^~~~~~~~~~
remote: /app/.build/checkouts/S3/Sources/S3/Extensions/S3+Private.swift:27:29: error: use of undeclared type 'URLRequest'
remote: func execute(_ request: URLRequest, on container: Container) -> Future {
remote: ^~~~~~~~~~
remote: /app/.build/checkouts/S3/Sources/S3/Extensions/S3+Private.swift:51:58: error: 'HTTPURLResponse' is unavailable: This type has moved to the FoundationNetworking module. Import that module to use it.
remote: static func convert(foundationResponse httpResponse: HTTPURLResponse, data: Data?, on worker: Worker) -> HTTPResponse {
remote: ^~~~~~~~~~~~~~~
remote: Foundation.HTTPURLResponse:2:18: note: 'HTTPURLResponse' has been explicitly marked unavailable here
remote: public typealias HTTPURLResponse = AnyObject
remote: ^
remote: /app/.build/checkouts/S3/Sources/S3/Extensions/S3+Private.swift:16:23: error: use of unresolved identifier 'URLRequest'
remote: var request = URLRequest(url: url, cachePolicy: cachePolicy)
remote: ^~~~~~~~~~
remote: /app/.build/checkouts/S3/Sources/S3/Extensions/S3+Private.swift:30:9: error: 'URLSession' is unavailable: This type has moved to the FoundationNetworking module. Import that module to use it.
remote: URLSession.shared.dataTask(with: request, completionHandler: { (data, urlResponse, error) in
remote: ^~~~~~~~~~
remote: Foundation.URLSession:2:18: note: 'URLSession' has been explicitly marked unavailable here
remote: public typealias URLSession = AnyObject

Custom S3 url

Is it possible to use a custom S3 URL, to use it with different S3 compatible storage providers e.g. Digital Ocean Spaces or self hosted Minio/CephFS?

Status: 307

When i am trying upload file like:
try s3.put(bytes: bytes, filePath: "example/example.txt")
i always get an error S3.Error: badResponse 307.

Issue with RC tag names

I may be wrong here - but why are all the tag names so inconsistent?

v3.0.0-rc2
3.0.0-RC3.0.1
3.0.0-RC3
3.0.0-RC2

Swift package manager doesn't like this.. I also don't think "v" should not be in the tag name either. rc2 shouldn't come after RC3.0.1. Thoughts?

For now, I'm just forking it and releasing a tag that works for swift PM.

`get` and `delete` cache issue?

I am working on file management abstraction for Swift and Vapor 3 and I have found probably a bug in cache/header management.

On getting file and immediately after that trying to delete it or even after running it second time, I get on get or delete this:

errorResponse(NIOHTTP1.HTTPResponseStatus.notImplemented, S3.ErrorMessage(code: "NotImplemented", message: "A header you provided implies functionality that is not implemented", bucket: nil, endpoint: nil, header: Optional("If-Modified-Since"), requestId: Optional("69D5F968DFC541E3"), hostId: Optional("MpD4rDbYQxpw5gOzln5q3mOlTEhmW8kqVCx+5bk9+JOyx26bOv2IznWqwN554TEWwqMvQ+eu/5k=")))

You can see by pulling the project, fetching deps and running tests: https://github.com/zdnk/vapor-filesystem/tree/4f6187b1c8d7306e78c1dbb5d74d752acf0c97db
Commit: 4f6187b1c8d7306e78c1dbb5d74d752acf0c97db

I am using there the latest RC of version 3 as you can see in Package files.

Presigned URL returns 403

Hi!

First of all, thanks for the library - upload is already working :-)

However, I have some problems when generating a presigned url, which always leads to 403 errors. Is this the correct way to do it?

let s3Client = try request.make(S3Client.self)
                  
let url = try s3Client.url(fileInfo: pathToFile, on: request)

let s3Signer = try request.make(S3Signer.self)

let presignedUrl = try s3Signer.presignedURL(for: .GET, url: url, expiration: Expiration.threeHours)

I get back this url:
https://s3.<region>.amazonaws.com/<bucket>/path/to/file.mp3?x-amz-algorithm=AWS4-HMAC-SHA256&x-amz-credential=<credentials>&x-amz-date=<date>&x-amz-expires=<date>&x-amz-signedheaders=host&x-amz-signature=<credentials>

When the file is public it works, but for private files I get the 403 error.

XAmzContentSHA256Mismatch

Hello, I am not sure what happend but after some time this library was working fine, I am starting getting issues on linux. This issues do not happen when compiled on macOS local machine, just getting them when running in Ubuntu 18.04

errorResponse(NIOHTTP1.HTTPResponseStatus.badRequest, S3.ErrorMessage(code: "XAmzContentSHA256Mismatch", message: "The provided 'x-amz-content-sha256' header does not match what was computed.", bucket: nil, endpoint: nil

XMLCoding cannot build on Ubuntu 16.04

Hi, the error messages

/usr/src/app/.build/checkouts/XMLCoding.git-5705070599758634965/Sources/XMLCoding/Encoder/XMLEncoder.swift:922:26: error: cannot convert value of type 'String' to type 'NSObject' in coercion
            return value.base64EncodedString() as NSObject
                   ~~~~~~^~~~~~~~~~~~~~~~~~~~~

Deploying to linux issue

error: cannot convert value of type 'NSMutableCharacterSet' to type 'CharacterSet' in coercion
return self.addingPercentEncoding(withAllowedCharacters: allowed as CharacterSet)

I experience this is when trying to deploy to remote via heroku

Conform S3.Error to AbortError Protocol

By conforming S3.Error to Vapor's AbortError protocol, the client would receive better error messages when a problem occurs. I don't have time for a PR, but this is what I managed to throw together:

extension S3.Error: AbortError {
    public var status: HTTPResponseStatus {
        switch self {
        case .s3NotRegistered: return .internalServerError
        case .notFound: return .notFound
        case .missingData: return .failedDependency
        case .invalidUrl: return .badRequest
        case .badStringData: return .badRequest
        case let .badResponse(response): return response.http.status
        case let .errorResponse(status, _): return status
        }
    }
    
    public var reason: String {
        switch self {
        case .s3NotRegistered: return "No S3 instance found registered with the current container"
        case .notFound: return "Resource not found at URL"
        case .missingData: return "Unexpectedly got empty response body from S3 API"
        case .invalidUrl: return "Cannot convert string to URL"
        case .badStringData: return "Cannot convert specified string to byte array"
        case let .badResponse(response): return String(data: response.http.body.data ?? Data(), encoding: .utf8) ?? ""
        case let .errorResponse(_, message): return "[" + message.code + "] " + message.message
        }
    }
    
    public var identifier: String {
        switch self {
        case .s3NotRegistered: return "s3NotRegistered"
        case .notFound: return "notFound"
        case .missingData: return "missingData"
        case .invalidUrl: return "invalidUrl"
        case .badStringData: return "badStringData"
        case .badResponse: return "badResponse"
        case .errorResponse: return "errorResponse"
        }
    }
}

Uploading fails on Linux/Docker in production mode

When trying to upload to S3 on a linux + production environment, an error is thrown;

The provided 'x-amz-content-sha256' header does not match what was computed.

This might have something to do with sending the Content-Length header, as suggested here

I'm using Swift 4.2 with Vapor 3.3.0, and the 'master' branch from this repo (might be nice to tag a new release #35 )

Testing the removal of the Content-Length header on my fork, will create a PR when this fixes things :)

Problem with package version in packages.swift

The problem is that when you want to add the module to your Vapor project you should write:
.package(url: "https://github.com/LiveUI/S3.git", .branch("master")), instead of your version - there are no last fixes on version which you suggest.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.