Home

Awesome

feathers-s3

Latest Release Build Status Code Climate Test Coverage License: MIT Download Status

feathers-s3 allows to deal with AWS S3 API compatible storages to manage file upload/download in a FeathersJS application.

Unlike the solution feathers-blob, which provides a store abstraction, feathers-s3 is limited to be used with stores providing a S3 compatible API. However, it takes advantage of the S3 API by using presigned URLs to manage (upload, share) objects on a store in a more reliable and secure way.

Using Presigned URL has different pros and cons:

To address these drawbacks, feathers-s3 provides:

Principle

The following sections illustrate the different process implemented by feathers-s3:

Upload

The upload process can be a singlepart upload or a multipart upload depending on the size of the object to be uploaded. If the size is greater than a chunkSize (by default 5MB), feathers-s3 performs a multipart upload. Otherwise it performs a singlepart upload.

Singlepart upload

Upload principle

Multipart upload

Mulitpart upload principle

Download

Donwload principle

Usage

Installation

npm install @kalisio/feathers-s3 --save

or

yarn add @kalisio/feathers-s3

Example

The provided example illustrates how to setup:

https://github.com/kalisio/feathers-s3/blob/fa8adde8181ccad740ed7fcfb92af1a5c43f9281/example/app.mjs#L1-L44

https://github.com/kalisio/feathers-s3/blob/184802600263e62b4d8d910d8781cbf3b7b03966/example/public/index.html#L1-L74

As a general rule you can also have a look at the tests in order to have different use cases of the library.

Data processing

Some use cases might require you directly process the data on your server before sending it to the object storage, e.g. if you'd like to resize an image. You can do that by:

  1. registering a before hook on the putObject custom method to process the data before sending it to the object storage,
  2. using the proxy mode on the client side service to send the data to your server instead of the object storage,
  3. defining the appropriate chunkSize on the client service to not use multipart upload as processing usually requires the whole content to be sent.

Here is a simple example relying on sharp to resize an image:

async function resizeImage (hook) {
  hook.data.buffer = await sharp(hook.data.buffer)
    .resize(128, 48, { fit: 'contain', background: '#00000000' }).toBuffer()
}

app.service('s3').hooks({
  before: {
    putObject: [resizeImage]
  }
})

// Here you can proceed as usual from server side
service.putObject({ id, buffer, type })
// or client side
clientService.upload(id, blob, options)

API

feathers-s3 consists of three parts:

Service

constructor (options)

Create an instance of the service with the given options:

ParameterDescriptionRequired
s3Clientthe s3Client configuration.yes
bucketthe bucket to use.yes
prefixan optional prefix to use when computing the final Keyno
btoathe binary to ascii function used to transform sent data into a string. Default is to transform to base64.no
atobthe ascii to binary function used to transform received data into a Buffer. Default is to transform back from base64.no

find (params)

Lists some objects in a bucket according given criteria provided in the params.query object.

Check the ListObjectsCommandInput documentation to have the list of supported poperties.

create (data, params)

Generates a presigned URL for the following commands:

The payload data must contain the following properties:

PropertyDescription
commandthe command for which the presigned URL should be created. The possible values are GetObject, PutObject and UploadPart.
idthe object key. Note that the final computed Key takes into account the prefix option of the service.
UploadIdthe UploadId generated when calling the createMultipartUpload method. It is required if the command is UploadPart
PartNumberthe PartNumber of the part to be uploaded. It is required if the command is UploadPart

get (id, params)

Get an object content from a bucket.

ParameterDescription
idthe object key. Note that the final computed Key takes into account the prefix option of the service.

[!NOTE] The object will be entirely read and transferred to the client, for large files consider using presigned URLs instead.

remove (id, params)

Remove an object fromt the bucket.

ParameterDescription
idthe object key. Note that the final computed Key takes into account the prefix option of the service.

createMultipartUpload (data, params)

Initiate a multipart upload.

It wraps the CreateMultipartUploadCommand.

The payload data must contain the following properties:

PropertyDescription
idthe object key. Note that the final computed Key takes into account the prefix option of the service.
typethe content type to be uploaded.

Any optional properties are forwarded to the underlying CreateMultipartUploadCommand command parameters.

completeMultipartUpload (data, params)

Finalize a multipart upload.

It wraps the CompleteMultipartUploadCommand.

The payload data must contain the following properties:

PropertyDescription
idthe object key. Note that the final computed Key takes into account the prefix option of the service.
UploadIdthe UploadId generated when calling the createMultipartUpload method.
partsthe uploaded parts. It consists in an array of objects following the schema: { PartNumber: <number>, ETag: <etag> )}.

Any optional properties are forwarded to the underlying CompleteMultipartUploadCommand command parameters.

uploadPart (data, params)

Upload a part to a bucket.

The payload data must contain the following properties:

PropertyDescription
idthe object key. Note that the final computed Key takes into account the prefix option of the service.
UploadIdthe UploadId generated when calling the createMultipartUpload method.
PartNumberthe part number.
bufferthe content to be uploaded.
typethe content type to be uploaded.

putObject (data, params)

Upload an object to a bucket.

The payload data must contain the following properties:

PropertyDescription
idthe object key. Note that the final computed Key takes into account the prefix option of the service.
bufferthe content to be uploaded.
typethe content type to be uploaded.

getObjectCommand (data, params)

Execute the GetObjectCommand and returns the response.

[!NOTE] This method is not declared on the client side.

The payload data must contain the following properties:

PropertyDescription
idthe object key. Note that the final computed Key takes into account the prefix option of the service.

uploadFile (data, params)

Convenient method to upload a file.

[!NOTE] This method is not declared on the client side.

The payload data must contain the following properties:

PropertyDescription
filePaththe path to the file to be uploaded. The basename is used for computing the object key.
contentTypethe content type of the file to be uploaded.

[!NOTE] You can also provide an id property to override the computed object key.

downloadFile (data, params)

Convenient method to download a file.

[!NOTE] This method is not declared on the client side.

The payload data must contain the following properties:

PropertyDescription
ìdthe file key. Note that the final computed Key takes into account the prefix option of the service.
filePaththe path to the downloaded file.

Middlewares

getObject (service)

It expect to be setup on a route path like path/to/get/* where the last parameter is the path to the object in the target bucket. It is associated with an S3 service to use the same configuration (s3client, bucket, etc...).

ArgumentDescriptionRequired
servicethe service to be associated to this midlleware.yes
// How to setup the route
app.get('/s3-objects/*', getObject(service))
// How to use it with any request agent like superagent
const response = await superagent.get('your.domain.com/s3-objects/my-file.png')

You can also simply use the target object URL in your HTML: <img src="your.domain.com/s3-objects/my-file.png">.

If you'd like to authenticate the route on your backend you will have to do something like this:

import { authenticate } from '@feathersjs/express'

app.get('/s3-objects/*', authenticate('jwt'), getObject(service))

In this case, if you need to reference the object by the URL it will require you to add the JWT as a query parameter like this: <img src="your.domain.com/s3-objects/my-file.png?jwt=TOKEN">. The JWT can then be extracted by a middleware:

import { authenticate } from '@feathersjs/express'

app.get('/s3-objects/*', (req, res, next) => {
  req.feathers.authentication = {
    strategy: 'jwt',
    accessToken: req.query.jwt
  }
  next()
}, authenticate('jwt'), getObject(service))

Client

getClientService (app, options)

Return the client service interface. The client service exposes the custom methods defined in the Service and is also decorated with 2 helper functions that really simplify the logic when implementing a client application, notably for multipart upload.

ArgumentDescriptionRequired
appthe Feathers client applicationyes
optionsthe options to pass to the client serviceno

The options are:

OptionsDescriptionDefault
transportthe transport layer used by the Feathers client application. For now it is required.
servicePaththe path to the service.s3
chunkSizethe size of the chunk to perfom multipart upload.5MB
useProxydefine whether to use backend as a proxy for custom methods.false
fetchthe fetch function.browser fetch function
btoathe binary to ascii function used to transform sent data into a string.transform to base64
atobthe ascii to binary function used to transform received data into a Buffer.transform from base64
debugthe debug function.null

upload (id, blob, options)

Upload a Blob object to the bucket with the given key id.

According the size of chunk you set when instanciang the client service and the size of the blob, the method will automatically perform a singlepart upload or a mulitpart upload.

If the proxy option is undefined. The client performs the upload action directly using fetch. Otherwise, it uses the proxyUpload custom method.

ArgumentDescriptionRequired
idthe object key. Note that the final computed Key takes into account the prefix option of the service.yes
blobthe content of the object to be uploaded defined as a Blob.yes
optionsoptions to be forwarded to the underlying service methods.no

download (key, type, options)

Download an object from the bucket with the given key id.

If the proxy option is undefined. The client performs the download action directly using fetch. Otherwise, it uses the getObject custom method.

ArgumentDescriptionRequired
idthe object key. Note that the final computed Key takes into account the prefix option of the service.yes
typethe type of the content to be downloaded.yes
optionsoptions to be forwarded to the underlying service methods.no

License

Copyright (c) 2017-20xx Kalisio

Licensed under the MIT license.

Authors

This project is sponsored by

Kalisio