uppy-s3_multipart 0.3.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +123 -121
- data/lib/uppy/s3_multipart/app.rb +18 -9
- data/lib/uppy/s3_multipart/client.rb +1 -7
- data/uppy-s3_multipart.gemspec +1 -1
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 127c554611e36c8802a60174dfbd624bab6de29aaa5f1ccdd04b71c2224f108e
|
4
|
+
data.tar.gz: 6950b75f85c04b03e973ae9581f59ec8defc4d1be7c5c50080fcd638c71adf97
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 292d94dbb74c0efe2108f041207e86a5d46b99d63fb85f4cf2ccc5c252d47c00507b6118bdfd995ee762049fc389705982cfc566ddc1f906b7956185179db1fd
|
7
|
+
data.tar.gz: 305fb7fea58a49a3768ebda006c5827b9b3911006087bb61f623a2519b10f5c680a39ab0578db085823963d2046753f3f4b294a4716c3e6e836b3c18173c1999
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Uppy::S3Multipart
|
2
2
|
|
3
|
-
Provides a Rack application that implements endpoints for the [
|
3
|
+
Provides a Rack application that implements endpoints for the [aws-s3-multipart]
|
4
4
|
Uppy plugin. This enables multipart uploads directly to S3, which is
|
5
5
|
recommended when dealing with large files, as it allows resuming interrupted
|
6
6
|
uploads.
|
@@ -10,37 +10,49 @@ uploads.
|
|
10
10
|
Add the gem to your Gemfile:
|
11
11
|
|
12
12
|
```rb
|
13
|
-
gem "uppy-s3_multipart", "~> 0
|
13
|
+
gem "uppy-s3_multipart", "~> 1.0"
|
14
14
|
```
|
15
15
|
|
16
16
|
## Setup
|
17
17
|
|
18
|
-
In order to allow direct multipart uploads to your S3 bucket,
|
18
|
+
In order to allow direct multipart uploads to your S3 bucket, you need to update
|
19
19
|
the bucket's CORS configuration. In the AWS S3 Console go to your bucket, click
|
20
20
|
on "Permissions" tab and then on "CORS configuration". There paste in the
|
21
21
|
following:
|
22
22
|
|
23
|
-
```
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
23
|
+
```json
|
24
|
+
[
|
25
|
+
{
|
26
|
+
"AllowedHeaders": [
|
27
|
+
"content-type",
|
28
|
+
"x-amz-content-sha256",
|
29
|
+
"x-amz-date"
|
30
|
+
],
|
31
|
+
"AllowedMethods": [
|
32
|
+
"GET",
|
33
|
+
"POST",
|
34
|
+
"PUT"
|
35
|
+
],
|
36
|
+
"AllowedOrigins": [
|
37
|
+
"https://my-app.com"
|
38
|
+
],
|
39
|
+
"ExposeHeaders": [
|
40
|
+
"ETag"
|
41
|
+
],
|
42
|
+
"MaxAgeSeconds": 3000
|
43
|
+
},
|
44
|
+
{
|
45
|
+
"AllowedHeaders": [],
|
46
|
+
"AllowedMethods": [
|
47
|
+
"GET"
|
48
|
+
],
|
49
|
+
"AllowedOrigins": [
|
50
|
+
"*"
|
51
|
+
],
|
52
|
+
"ExposeHeaders": [],
|
53
|
+
"MaxAgeSeconds": 3000
|
54
|
+
}
|
55
|
+
]
|
44
56
|
```
|
45
57
|
|
46
58
|
Replace `https://my-app.com` with the URL to your app (in development you can
|
@@ -51,11 +63,63 @@ CORS settings to be applied.
|
|
51
63
|
|
52
64
|
This gem provides a Rack application that you can mount inside your main
|
53
65
|
application. If you're using [Shrine], you can initialize the Rack application
|
54
|
-
via the
|
55
|
-
|
66
|
+
via the [Shrine plugin](#shrine).
|
67
|
+
|
68
|
+
### App
|
69
|
+
|
70
|
+
At its core, you initialize an `Uppy::S3Multipart::App` with an
|
71
|
+
`Aws::S3::Bucket` object:
|
72
|
+
|
73
|
+
```rb
|
74
|
+
require "uppy/s3_multipart"
|
75
|
+
|
76
|
+
bucket = Aws::S3::Bucket.new(
|
77
|
+
name: "my-bucket",
|
78
|
+
access_key_id: "...",
|
79
|
+
secret_access_key: "...",
|
80
|
+
region: "...",
|
81
|
+
)
|
82
|
+
|
83
|
+
UPPY_S3_MULTIPART_APP = Uppy::S3Multipart::App.new(bucket: bucket)
|
84
|
+
```
|
85
|
+
|
86
|
+
The instance of `Uppy::S3Multipart::App` is a Rack application that can be
|
87
|
+
mounted in your router (`config/routes.rb` in Rails). It should be
|
88
|
+
mounted at `/s3/multipart`:
|
89
|
+
|
90
|
+
```rb
|
91
|
+
# config/routes.rb (Rails)
|
92
|
+
Rails.application.routes.draw do
|
93
|
+
# ...
|
94
|
+
mount UPPY_S3_MULTIPART_APP => "/s3/multipart"
|
95
|
+
end
|
96
|
+
```
|
97
|
+
|
98
|
+
This will add the routes that the `aws-s3-multipart` Uppy plugin expects:
|
99
|
+
|
100
|
+
```
|
101
|
+
POST /s3/multipart
|
102
|
+
GET /s3/multipart/:uploadId
|
103
|
+
GET /s3/multipart/:uploadId/:partNumber
|
104
|
+
POST /s3/multipart/:uploadId/complete
|
105
|
+
DELETE /s3/multipart/:uploadId
|
106
|
+
```
|
107
|
+
|
108
|
+
Since your app will now play the role of Uppy Companion, in your Uppy
|
109
|
+
configuration you can point `companionUrl` to your app's URL:
|
110
|
+
|
111
|
+
```js
|
112
|
+
// ...
|
113
|
+
uppy.use(Uppy.AwsS3Multipart, {
|
114
|
+
companionUrl: '/',
|
115
|
+
})
|
116
|
+
```
|
56
117
|
|
57
118
|
### Shrine
|
58
119
|
|
120
|
+
If you're using Shrine, you can use the `uppy_s3_multipart` Shrine plugin that
|
121
|
+
ships with this gem to simplify the setup.
|
122
|
+
|
59
123
|
In your Shrine initializer load the `uppy_s3_multipart` plugin:
|
60
124
|
|
61
125
|
```rb
|
@@ -71,39 +135,34 @@ Shrine.storages = {
|
|
71
135
|
Shrine.plugin :uppy_s3_multipart # load the plugin
|
72
136
|
```
|
73
137
|
|
74
|
-
The plugin will provide a `Shrine.uppy_s3_multipart` method that creates
|
75
|
-
`Uppy::S3Multipart::App` instance, which
|
76
|
-
inside your main application:
|
138
|
+
The plugin will provide a `Shrine.uppy_s3_multipart` method that creates the
|
139
|
+
`Uppy::S3Multipart::App` instance, which you can then mount inside your router:
|
77
140
|
|
78
141
|
```rb
|
79
|
-
#
|
142
|
+
# config/routes.rb (Rails)
|
80
143
|
Rails.application.routes.draw do
|
144
|
+
# ...
|
81
145
|
mount Shrine.uppy_s3_multipart(:cache) => "/s3/multipart"
|
82
146
|
end
|
83
|
-
|
84
|
-
# Rack (config.ru)
|
85
|
-
map "/s3/multipart" do
|
86
|
-
run Shrine.uppy_s3_multipart(:cache)
|
87
|
-
end
|
88
147
|
```
|
89
148
|
|
90
|
-
Now in your Uppy configuration point `
|
149
|
+
Now in your Uppy configuration point `companionUrl` to your app's URL:
|
91
150
|
|
92
151
|
```js
|
93
152
|
// ...
|
94
153
|
uppy.use(Uppy.AwsS3Multipart, {
|
95
|
-
|
154
|
+
companionUrl: '/',
|
96
155
|
})
|
97
156
|
```
|
98
157
|
|
99
|
-
In the `upload-success` Uppy callback you can
|
100
|
-
|
101
|
-
|
158
|
+
In the `upload-success` Uppy callback, you can construct the Shrine uploaded
|
159
|
+
file data (this example assumes your temporary Shrine S3 storage has `prefix:
|
160
|
+
"cache"` set):
|
102
161
|
|
103
162
|
```js
|
104
|
-
uppy.on('upload-success', function (file,
|
163
|
+
uppy.on('upload-success', function (file, response) {
|
105
164
|
var uploadedFileData = JSON.stringify({
|
106
|
-
id: uploadURL.match(/\/cache\/([^\?]+)/)[1], // extract key without prefix
|
165
|
+
id: response.uploadURL.match(/\/cache\/([^\?]+)/)[1], // extract key without prefix
|
107
166
|
storage: 'cache',
|
108
167
|
metadata: {
|
109
168
|
size: file.size,
|
@@ -115,108 +174,57 @@ uppy.on('upload-success', function (file, data, uploadURL) {
|
|
115
174
|
})
|
116
175
|
```
|
117
176
|
|
118
|
-
|
119
|
-
Shrine. From there you can swap the `presign_endpoint` + `
|
120
|
-
`uppy_s3_multipart` + `
|
177
|
+
See [Adding Direct S3 Uploads] for an example of a complete Uppy setup with
|
178
|
+
Shrine. From there you can swap the `presign_endpoint` + `aws-s3` code with the
|
179
|
+
`uppy_s3_multipart` + `aws-s3-multipart` setup.
|
121
180
|
|
122
181
|
Note that **Shrine won't extract metadata from directly upload files on
|
123
182
|
assignment** by default. Instead, it will just copy metadata that was extracted
|
124
183
|
on the client side. See [this section][metadata direct uploads] for the
|
125
184
|
rationale and instructions on how to opt in.
|
126
185
|
|
127
|
-
|
128
|
-
|
129
|
-
You can also use `uppy-s3_multipart` without Shrine, by initializing the
|
130
|
-
`Uppy::S3Multipart::App` directly:
|
131
|
-
|
132
|
-
```rb
|
133
|
-
require "uppy/s3_multipart"
|
134
|
-
|
135
|
-
resource = Aws::S3::Resource.new(
|
136
|
-
access_key_id: "...",
|
137
|
-
secret_access_key: "...",
|
138
|
-
region: "...",
|
139
|
-
)
|
140
|
-
|
141
|
-
bucket = resource.bucket("my-bucket")
|
142
|
-
|
143
|
-
UPPY_S3_MULTIPART_APP = Uppy::S3Multipart::App.new(bucket: bucket)
|
144
|
-
```
|
145
|
-
|
146
|
-
You can mount it inside your main app in the same way:
|
147
|
-
|
148
|
-
```rb
|
149
|
-
# Rails (config/routes.rb)
|
150
|
-
Rails.application.routes.draw do
|
151
|
-
mount UPPY_S3_MULTIPART_APP => "/s3/multipart"
|
152
|
-
end
|
153
|
-
|
154
|
-
# Rack (config.ru)
|
155
|
-
map "/s3/multipart" do
|
156
|
-
run UPPY_S3_MULTIPART_APP
|
157
|
-
end
|
158
|
-
```
|
159
|
-
|
160
|
-
This will add the routes that the `AwsS3Multipart` Uppy plugin expects:
|
161
|
-
|
162
|
-
```
|
163
|
-
POST /s3/multipart
|
164
|
-
GET /s3/multipart/:uploadId
|
165
|
-
GET /s3/multipart/:uploadId/:partNumber
|
166
|
-
POST /s3/multipart/:uploadId/complete
|
167
|
-
DELETE /s3/multipart/:uploadId
|
168
|
-
```
|
169
|
-
|
170
|
-
Now in your Uppy configuration point `serverUrl` to your app's URL:
|
171
|
-
|
172
|
-
```js
|
173
|
-
// ...
|
174
|
-
uppy.use(Uppy.AwsS3Multipart, {
|
175
|
-
serverUrl: '/',
|
176
|
-
})
|
177
|
-
```
|
178
|
-
|
179
|
-
### Configuration
|
186
|
+
## Configuration
|
180
187
|
|
181
188
|
This section describe various configuration options that you can pass to
|
182
189
|
`Uppy::S3Multipart::App`.
|
183
190
|
|
184
191
|
#### `:bucket`
|
185
192
|
|
186
|
-
The `:bucket` option is mandatory and accepts an instance of `Aws::S3::Bucket
|
187
|
-
It's easiest to create an `Aws::S3::Resource`, and call `#bucket` on it.
|
193
|
+
The `:bucket` option is mandatory and accepts an instance of `Aws::S3::Bucket`:
|
188
194
|
|
189
195
|
```rb
|
190
196
|
require "uppy/s3_multipart"
|
191
197
|
|
192
|
-
|
198
|
+
bucket = Aws::S3::Bucket.new(
|
199
|
+
name: "<BUCKET>",
|
193
200
|
access_key_id: "<ACCESS_KEY_ID>",
|
194
201
|
secret_access_key: "<SECRET_ACCESS_KEY>",
|
195
202
|
region: "<REGION>",
|
196
203
|
)
|
197
204
|
|
198
|
-
bucket
|
199
|
-
|
200
|
-
Uppy::S3MUltipart::App.new(bucket: bucket)
|
205
|
+
Uppy::S3Multipart::App.new(bucket: bucket)
|
201
206
|
```
|
202
207
|
|
203
|
-
If you want to use [Minio], you can easily configure
|
204
|
-
|
208
|
+
If you want to use [Minio], you can easily configure the `Aws::S3::Bucket` to
|
209
|
+
use your Minio server:
|
205
210
|
|
206
211
|
```rb
|
207
|
-
|
212
|
+
bucket = Aws::S3::Bucket.new(
|
213
|
+
name: "<MINIO_BUCKET>",
|
208
214
|
access_key_id: "<MINIO_ACCESS_KEY>", # "AccessKey" value
|
209
215
|
secret_access_key: "<MINIO_SECRET_KEY>", # "SecretKey" value
|
210
216
|
endpoint: "<MINIO_ENDPOINT>", # "Endpoint" value
|
211
217
|
region: "us-east-1",
|
212
|
-
force_path_style: true,
|
213
218
|
)
|
214
219
|
|
215
|
-
bucket
|
220
|
+
Uppy::S3Multipart::App.new(bucket: bucket)
|
216
221
|
```
|
217
222
|
|
218
|
-
|
219
|
-
|
223
|
+
Except for `:name`, all options passed to [`Aws::S3::Bucket#initialize`] are
|
224
|
+
forwarded to [`Aws::S3::Client#initialize`], see its documentation for
|
225
|
+
additional options.
|
226
|
+
|
227
|
+
In the Shrine plugin this configuration is inferred from the S3 storage.
|
220
228
|
|
221
229
|
#### `:prefix`
|
222
230
|
|
@@ -227,14 +235,7 @@ to be uploaded to.
|
|
227
235
|
Uppy::S3Multipart::App.new(bucket: bucket, prefix: "cache")
|
228
236
|
```
|
229
237
|
|
230
|
-
In the Shrine plugin this option is inferred from the S3 storage
|
231
|
-
|
232
|
-
```rb
|
233
|
-
Shrine.storages = {
|
234
|
-
cache: Shrine::Storage::S3.new(prefix: "cache", **options),
|
235
|
-
store: Shrine::Storage::S3.new(**options),
|
236
|
-
}
|
237
|
-
```
|
238
|
+
In the Shrine plugin this option is inferred from the S3 storage.
|
238
239
|
|
239
240
|
#### `:options`
|
240
241
|
|
@@ -326,7 +327,7 @@ Shrine.storages = {
|
|
326
327
|
}
|
327
328
|
```
|
328
329
|
|
329
|
-
|
330
|
+
## Client
|
330
331
|
|
331
332
|
If you would rather implement the endpoints yourself, you can utilize the
|
332
333
|
`Uppy::S3Multipart::Client` to make S3 requests.
|
@@ -475,13 +476,14 @@ Covenant](http://contributor-covenant.org) code of conduct.
|
|
475
476
|
The gem is available as open source under the terms of the [MIT
|
476
477
|
License](https://opensource.org/licenses/MIT).
|
477
478
|
|
478
|
-
[
|
479
|
+
[aws-s3-multipart]: https://uppy.io/docs/aws-s3-multipart/
|
479
480
|
[Shrine]: https://shrinerb.com
|
480
481
|
[Adding Direct S3 Uploads]: https://github.com/shrinerb/shrine/wiki/Adding-Direct-S3-Uploads
|
481
482
|
[Minio]: https://minio.io/
|
482
483
|
[Client]: #client
|
483
484
|
[content_disposition]: https://github.com/shrinerb/content_disposition
|
484
485
|
[`Rack::Request`]: https://www.rubydoc.info/github/rack/rack/master/Rack/Request
|
486
|
+
[`Aws::S3::Bucket#initialize`]: https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Bucket.html#initialize-instance_method
|
485
487
|
[`Aws::S3::Client#initialize`]: https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html#initialize-instance_method
|
486
488
|
[`Aws::S3::Client#create_multipart_upload`]: https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html#create_multipart_upload-instance_method
|
487
489
|
[`Aws::S3::Client#list_parts`]: https://docs.aws.amazon.com/sdk-for-ruby/v3/api/Aws/S3/Client.html#list_parts-instance_method
|
@@ -20,6 +20,11 @@ module Uppy
|
|
20
20
|
@router.call(env)
|
21
21
|
end
|
22
22
|
|
23
|
+
def inspect
|
24
|
+
"#<Uppy::S3Multipart::App>"
|
25
|
+
end
|
26
|
+
alias to_s inspect
|
27
|
+
|
23
28
|
class Router < Roda
|
24
29
|
plugin :all_verbs
|
25
30
|
plugin :json
|
@@ -33,23 +38,27 @@ module Uppy
|
|
33
38
|
route do |r|
|
34
39
|
# POST /s3/multipart
|
35
40
|
r.post ["", true] do
|
36
|
-
|
37
|
-
filename
|
38
|
-
|
39
|
-
extension = File.extname(filename.to_s)
|
40
|
-
key = SecureRandom.hex + extension
|
41
|
-
key = "#{opts[:prefix]}/#{key}" if opts[:prefix]
|
41
|
+
type = r.params["type"]
|
42
|
+
filename = r.params["filename"]
|
42
43
|
|
43
|
-
|
44
|
+
key = SecureRandom.hex + File.extname(filename.to_s)
|
45
|
+
key = [*opts[:prefix], key].join("/")
|
44
46
|
|
45
|
-
options = {
|
46
|
-
options[:
|
47
|
+
options = {}
|
48
|
+
options[:content_type] = type if type
|
49
|
+
options[:content_disposition] = ContentDisposition.inline(filename) if filename
|
50
|
+
options[:acl] = "public-read" if opts[:public]
|
47
51
|
|
48
52
|
result = client_call(:create_multipart_upload, key: key, **options)
|
49
53
|
|
50
54
|
{ uploadId: result.fetch(:upload_id), key: result.fetch(:key) }
|
51
55
|
end
|
52
56
|
|
57
|
+
# OPTIONS /s3/multipart
|
58
|
+
r.options ["", true] do
|
59
|
+
r.halt 204
|
60
|
+
end
|
61
|
+
|
53
62
|
# GET /s3/multipart/:uploadId
|
54
63
|
r.get String do |upload_id|
|
55
64
|
key = param!("key")
|
@@ -56,13 +56,7 @@ module Uppy
|
|
56
56
|
|
57
57
|
def abort_multipart_upload(upload_id:, key:, **options)
|
58
58
|
multipart_upload = multipart_upload(upload_id, key)
|
59
|
-
|
60
|
-
# aws-sdk-s3 docs recommend retrying the abort in case the multipart
|
61
|
-
# upload still has parts
|
62
|
-
loop do
|
63
|
-
multipart_upload.abort(**options)
|
64
|
-
break unless multipart_upload.parts.any?
|
65
|
-
end
|
59
|
+
multipart_upload.abort(**options)
|
66
60
|
|
67
61
|
{}
|
68
62
|
end
|
data/uppy-s3_multipart.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: uppy-s3_multipart
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Janko Marohnić
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-07-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: roda
|
@@ -142,7 +142,7 @@ dependencies:
|
|
142
142
|
- - "~>"
|
143
143
|
- !ruby/object:Gem::Version
|
144
144
|
version: '3.23'
|
145
|
-
description:
|
145
|
+
description:
|
146
146
|
email:
|
147
147
|
- janko.marohnic@gmail.com
|
148
148
|
executables: []
|
@@ -161,7 +161,7 @@ homepage: https://github.com/janko/uppy-s3_multipart
|
|
161
161
|
licenses:
|
162
162
|
- MIT
|
163
163
|
metadata: {}
|
164
|
-
post_install_message:
|
164
|
+
post_install_message:
|
165
165
|
rdoc_options: []
|
166
166
|
require_paths:
|
167
167
|
- lib
|
@@ -176,8 +176,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
176
176
|
- !ruby/object:Gem::Version
|
177
177
|
version: '0'
|
178
178
|
requirements: []
|
179
|
-
rubygems_version: 3.
|
180
|
-
signing_key:
|
179
|
+
rubygems_version: 3.2.15
|
180
|
+
signing_key:
|
181
181
|
specification_version: 4
|
182
182
|
summary: Provides a Rack application that implements endpoints for the AwsS3Multipart
|
183
183
|
Uppy plugin.
|