shrine 2.3.1 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of shrine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +212 -89
- data/doc/attacher.md +227 -0
- data/doc/design.md +5 -19
- data/doc/paperclip.md +2 -1
- data/doc/testing.md +266 -0
- data/lib/shrine.rb +220 -168
- data/lib/shrine/plugins/activerecord.rb +23 -14
- data/lib/shrine/plugins/backgrounding.rb +3 -3
- data/lib/shrine/plugins/cached_attachment_data.rb +6 -5
- data/lib/shrine/plugins/copy.rb +10 -9
- data/lib/shrine/plugins/data_uri.rb +5 -0
- data/lib/shrine/plugins/default_url_options.rb +17 -4
- data/lib/shrine/plugins/direct_upload.rb +6 -11
- data/lib/shrine/plugins/download_endpoint.rb +8 -24
- data/lib/shrine/plugins/multi_delete.rb +1 -1
- data/lib/shrine/plugins/processing.rb +8 -0
- data/lib/shrine/plugins/remote_url.rb +5 -0
- data/lib/shrine/plugins/remove_attachment.rb +3 -10
- data/lib/shrine/plugins/sequel.rb +28 -25
- data/lib/shrine/plugins/versions.rb +12 -1
- data/lib/shrine/storage/file_system.rb +16 -14
- data/lib/shrine/storage/linter.rb +6 -0
- data/lib/shrine/storage/s3.rb +51 -25
- data/lib/shrine/version.rb +2 -2
- data/shrine.gemspec +2 -2
- metadata +8 -7
- data/lib/shrine/plugins/concatenation.rb +0 -73
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b4039e8d8350766e0bbe1555e0039672f4224915
|
4
|
+
data.tar.gz: b8200b69674d031d15c82fd19e704eee55d049b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0cf6ec31cb22d1396575720a5a8543f71875e9a5c9851ea1f8311aec34a5ef1dc1c83be64b32434dac3e825dd3b52c06bd9035eb15a02fe11572cbceb9584c45
|
7
|
+
data.tar.gz: 49ac2e4d1065b37733c82b7dacb8498db1784a2c7c2dadfb643b8d2348d9b8fea0ca84062dafa1dc7ce836ef142a101202b83abe2870003a7d803dd2230ace5c
|
data/README.md
CHANGED
@@ -2,8 +2,8 @@
|
|
2
2
|
|
3
3
|
Shrine is a toolkit for file attachments in Ruby applications.
|
4
4
|
|
5
|
-
If you're
|
6
|
-
|
5
|
+
If you're not sure why you should care, you're encouraged to read the
|
6
|
+
[motivation behind creating Shrine][motivation].
|
7
7
|
|
8
8
|
## Resources
|
9
9
|
|
@@ -14,7 +14,8 @@ explains the motivation behind Shrine.
|
|
14
14
|
|
15
15
|
## Quick start
|
16
16
|
|
17
|
-
Add Shrine to the Gemfile and write an initializer
|
17
|
+
Add Shrine to the Gemfile and write an initializer which sets up the storage and
|
18
|
+
loads the ORM plugin:
|
18
19
|
|
19
20
|
```rb
|
20
21
|
gem "shrine"
|
@@ -25,16 +26,17 @@ require "shrine"
|
|
25
26
|
require "shrine/storage/file_system"
|
26
27
|
|
27
28
|
Shrine.storages = {
|
28
|
-
cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"),
|
29
|
-
store: Shrine::Storage::FileSystem.new("public", prefix: "uploads/store"),
|
29
|
+
cache: Shrine::Storage::FileSystem.new("public", prefix: "uploads/cache"), # temporary
|
30
|
+
store: Shrine::Storage::FileSystem.new("public", prefix: "uploads/store"), # permanent
|
30
31
|
}
|
31
32
|
|
32
33
|
Shrine.plugin :sequel # :activerecord
|
33
34
|
Shrine.plugin :cached_attachment_data # for forms
|
34
35
|
```
|
35
36
|
|
36
|
-
Next
|
37
|
-
|
37
|
+
Next decide how you will name the attachment attribute on your model, and run a
|
38
|
+
migration that adds an `<attachment>_data` text column, which Shrine will use
|
39
|
+
to store all information about the attachment:
|
38
40
|
|
39
41
|
```rb
|
40
42
|
Sequel.migration do # class AddImageDataToPhotos < ActiveRecord::Migration
|
@@ -45,7 +47,7 @@ end # end
|
|
45
47
|
```
|
46
48
|
|
47
49
|
Now you can create an uploader class for the type of files you want to upload,
|
48
|
-
and
|
50
|
+
and add the attachment attribute to your model which will accept files:
|
49
51
|
|
50
52
|
```rb
|
51
53
|
class ImageUploader < Shrine
|
@@ -59,8 +61,11 @@ class Photo < Sequel::Model # ActiveRecord::Base
|
|
59
61
|
end
|
60
62
|
```
|
61
63
|
|
62
|
-
|
63
|
-
|
64
|
+
Let's now add the form fields needed for attaching files. We need a file
|
65
|
+
field for choosing files, and a hidden field for retaining the uploaded file
|
66
|
+
in case of validation errors and [direct uploads]. Note that the file field
|
67
|
+
needs to go *after* the hidden field, so that attaching a new file can always
|
68
|
+
override whatever is in the hidden field.
|
64
69
|
|
65
70
|
```erb
|
66
71
|
<form action="/photos" method="post" enctype="multipart/form-data">
|
@@ -75,17 +80,22 @@ add the form fields needed for attaching files:
|
|
75
80
|
<% end %>
|
76
81
|
```
|
77
82
|
|
78
|
-
|
79
|
-
|
83
|
+
Note the `enctype="multipart/form-data"` HTML attribute, which is required for
|
84
|
+
submitting files through the form. The Rails form builder will automatically
|
85
|
+
generate it for you when you add a file field.
|
86
|
+
|
87
|
+
Now in your router/controller the attachment request parameter can be assigned
|
88
|
+
to the model like any other attribute. Note that for non-Rails apps you will
|
89
|
+
need to load the `rack_file` plugin which handles Rack's uploaded file hash.
|
80
90
|
|
81
91
|
```rb
|
82
92
|
post "/photos" do
|
83
93
|
Photo.create(params[:photo])
|
94
|
+
# ...
|
84
95
|
end
|
85
96
|
```
|
86
97
|
|
87
|
-
|
88
|
-
its URL:
|
98
|
+
Finally, you can use the URL of the attached file to display it:
|
89
99
|
|
90
100
|
```erb
|
91
101
|
<img src="<%= @photo.image_url %>">
|
@@ -93,7 +103,7 @@ its URL:
|
|
93
103
|
|
94
104
|
## Attachment
|
95
105
|
|
96
|
-
When we assign an IO
|
106
|
+
When we assign an IO object to the record, Shrine will upload it to the
|
97
107
|
registered `:cache` storage, which acts as a temporary storage, and write the
|
98
108
|
location, storage, and metadata of the uploaded file to a single
|
99
109
|
`<attachment>_data` column:
|
@@ -112,13 +122,17 @@ The Shrine attachment module added the following methods to the `Photo` model:
|
|
112
122
|
* `#image=` – caches the file and saves the result into `image_data`
|
113
123
|
* `#image` – returns `Shrine::UploadedFile` instantiated from `image_data`
|
114
124
|
* `#image_url` – calls `image.url` if attachment is present, otherwise returns nil
|
115
|
-
* `#image_attacher`
|
125
|
+
* `#image_attacher` – returns instance of `Shrine::Attacher` which handles attaching
|
116
126
|
|
117
127
|
In addition to assigning new files, you can also assign already cached files
|
118
128
|
using their JSON representation:
|
119
129
|
|
120
130
|
```rb
|
121
|
-
photo.image = '{
|
131
|
+
photo.image = '{
|
132
|
+
"storage": "cache",
|
133
|
+
"id": "9260ea09d8effd.jpg",
|
134
|
+
"metadata": { ... }
|
135
|
+
}'
|
122
136
|
```
|
123
137
|
|
124
138
|
This allows Shrine to retain uploaded files in case of validation errors, and
|
@@ -161,6 +175,29 @@ class Movie < Sequel::Model
|
|
161
175
|
end
|
162
176
|
```
|
163
177
|
|
178
|
+
### Attacher
|
179
|
+
|
180
|
+
The model attachment interface under-the-hood just delegates to a
|
181
|
+
`Shrine::Attacher` object. If whether you don't want to add additional methods
|
182
|
+
to your model, prefer explicitness over callbacks, or use Shrine with custom
|
183
|
+
models, you can use `Shrine::Attacher` directly:
|
184
|
+
|
185
|
+
```rb
|
186
|
+
attacher = ImageUploader::Attacher.new(photo, :image) # equivalent to `photo.image_attacher`
|
187
|
+
|
188
|
+
attacher.cache #=> #<ImageUploader @storage_key=:cache>
|
189
|
+
attacher.store #=> #<ImageUploader @storage_key=:store>
|
190
|
+
|
191
|
+
attacher.assign(file) # equivalent to `photo.image = file`
|
192
|
+
attacher.get # equivalent to `photo.image`
|
193
|
+
attacher.url # equivalent to `photo.image_url`
|
194
|
+
|
195
|
+
attacher.finalize # promotes cached file to store, deletes old attachment (after save callback)
|
196
|
+
attacher.destroy # deletes attachment (after destory callback)
|
197
|
+
```
|
198
|
+
|
199
|
+
See [Using Attacher] guide for more details.
|
200
|
+
|
164
201
|
### Multiple files
|
165
202
|
|
166
203
|
Sometimes we want to allow users to upload multiple files at once. This can be
|
@@ -179,11 +216,14 @@ into nested association attributes in the controller.
|
|
179
216
|
## Uploader
|
180
217
|
|
181
218
|
"Uploaders" are subclasses of `Shrine`, and this is where we define all our
|
182
|
-
attachment logic.
|
183
|
-
|
184
|
-
and are stateless; they are only in charge of uploading, processing and
|
185
|
-
deleting files.
|
219
|
+
attachment logic. Uploader objects act as a wrappers around a storage; they
|
220
|
+
don't know anything about models, and are stateless.
|
186
221
|
|
222
|
+
```rb
|
223
|
+
class DocumentUploader < Shrine
|
224
|
+
# document uploading logic
|
225
|
+
end
|
226
|
+
```
|
187
227
|
```rb
|
188
228
|
uploader = DocumentUploader.new(:store)
|
189
229
|
uploaded_file = uploader.upload(File.open("resume.pdf"))
|
@@ -191,6 +231,14 @@ uploaded_file #=> #<Shrine::UploadedFile>
|
|
191
231
|
uploaded_file.to_json #=> '{"storage":"store","id":"0sdfllasfi842.pdf","metadata":{...}}'
|
192
232
|
```
|
193
233
|
|
234
|
+
The `Shrine#upload` method does the following:
|
235
|
+
|
236
|
+
* calls processing
|
237
|
+
* extracts metadata
|
238
|
+
* generates unique location
|
239
|
+
* uploads file(s) (this is where the storage is called)
|
240
|
+
* closes uploaded file(s)
|
241
|
+
|
194
242
|
Shrine requires the input for uploading to be an IO-like object. So, `File`,
|
195
243
|
`Tempfile` and `StringIO` instances are all valid inputs. The object doesn't
|
196
244
|
have to be an actual IO, it's enough that it responds to: `#read(*args)`,
|
@@ -205,7 +253,7 @@ uploaded_file.url #=> "uploads/938kjsdf932.mp4"
|
|
205
253
|
uploaded_file.metadata #=> {...}
|
206
254
|
uploaded_file.download #=> #<Tempfile:/var/folders/k7/6zx6dx6x7ys3rv3srh0nyfj00000gn/T/20151004-74201-1t2jacf.mp4>
|
207
255
|
uploaded_file.exists? #=> true
|
208
|
-
uploaded_file.open { |io|
|
256
|
+
uploaded_file.open { |io| io.read }
|
209
257
|
uploaded_file.delete
|
210
258
|
# ...
|
211
259
|
```
|
@@ -224,8 +272,8 @@ and any additional features are available via plugins. This way you can choose
|
|
224
272
|
exactly what and how much Shrine does for you. See the [website] for a complete
|
225
273
|
list of plugins.
|
226
274
|
|
227
|
-
The plugin system respects inheritance, so you can choose
|
228
|
-
|
275
|
+
The plugin system respects inheritance, so you can choose to load a plugin
|
276
|
+
globally or only for a specific uploader.
|
229
277
|
|
230
278
|
```rb
|
231
279
|
Shrine.plugin :logging # enables logging for all uploaders
|
@@ -274,8 +322,8 @@ class ImageUploader < Shrine
|
|
274
322
|
end
|
275
323
|
```
|
276
324
|
|
277
|
-
|
278
|
-
a `File`,
|
325
|
+
Here the `io` is a cached `Shrine::UploadedFile`, so we need to download it to
|
326
|
+
a `File`, since this is what image_processing gem recognizes.
|
279
327
|
|
280
328
|
### Versions
|
281
329
|
|
@@ -305,11 +353,11 @@ class ImageUploader < Shrine
|
|
305
353
|
end
|
306
354
|
```
|
307
355
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
recommended to load the `delete_raw` plugin
|
312
|
-
files after uploading.
|
356
|
+
By defining processing on instance-level Shrine gives you a lot of flexibility.
|
357
|
+
You could choose the processing order which yields the best performance, even
|
358
|
+
add parallelization, and when processing logic gets complex you could extract
|
359
|
+
everything into a PORO class. It is recommended to load the `delete_raw` plugin
|
360
|
+
so that processed files are automatically deleted after uploading.
|
313
361
|
|
314
362
|
Each version will be saved to the attachment column, and the attachment getter
|
315
363
|
will simply return a Hash of `Shrine::UploadedFile` objects:
|
@@ -356,6 +404,41 @@ class VideoUploader < Shrine
|
|
356
404
|
end
|
357
405
|
```
|
358
406
|
|
407
|
+
### Triggering processing
|
408
|
+
|
409
|
+
Whenever a file is uploaded by the attacher, an additional `:action` parameter
|
410
|
+
is added to the context, which holds a symbol name describing what the file was
|
411
|
+
uploaded for. For example, for caching files `action: :cache` will be sent, for
|
412
|
+
promoting `action: :store`, while for backing up attacher sends `action:
|
413
|
+
:backup`.
|
414
|
+
|
415
|
+
The argument to the `process` declaration is the name of that action. When
|
416
|
+
uploading via the uploader, you can add `:action` option with the value of the
|
417
|
+
processing block you want performed before uploading.
|
418
|
+
|
419
|
+
```rb
|
420
|
+
uploader = ImageUploader.new(:store)
|
421
|
+
uploader.upload(file, action: :store) # performs processing defined under ":store"
|
422
|
+
```
|
423
|
+
|
424
|
+
You can also define and call processing for a custom action:
|
425
|
+
|
426
|
+
```rb
|
427
|
+
class ImageUploader < Shrine
|
428
|
+
process(:my_action) { |io, context| special_processing(io) }
|
429
|
+
end
|
430
|
+
```
|
431
|
+
```rb
|
432
|
+
uploader.upload(file, action: :my_action)
|
433
|
+
```
|
434
|
+
|
435
|
+
Finally, you can also call defined processing directly, without uploading the
|
436
|
+
results, using `Shrine#process`.
|
437
|
+
|
438
|
+
```rb
|
439
|
+
uploader.process(file, action: :my_action) # returns processed files without uploading
|
440
|
+
```
|
441
|
+
|
359
442
|
## Context
|
360
443
|
|
361
444
|
You may have noticed the `context` variable floating around as the second
|
@@ -371,57 +454,24 @@ to uploaded file, and can contain useful information depending on the situation:
|
|
371
454
|
The `context` is useful for doing conditional processing, validation,
|
372
455
|
generating location etc, and it is also used by some plugins internally.
|
373
456
|
|
374
|
-
## Validation
|
375
|
-
|
376
|
-
Validations are registered inside a `Attacher.validate` block, and you can load
|
377
|
-
the `validation_helpers` plugin to get some convenient file validation methods:
|
378
|
-
|
379
457
|
```rb
|
380
458
|
class VideoUploader < Shrine
|
381
|
-
|
382
|
-
|
383
|
-
Attacher.validate do
|
384
|
-
validate_max_size 50*1024*1024, message: "is too large (max is 50 MB)"
|
385
|
-
validate_mime_type_inclusion ["video/mp4"]
|
386
|
-
end
|
387
|
-
end
|
388
|
-
```
|
389
|
-
|
390
|
-
```rb
|
391
|
-
trailer = Trailer.new
|
392
|
-
trailer.video = File.open("matrix.mp4")
|
393
|
-
trailer.valid? #=> false
|
394
|
-
trailer.errors.to_hash #=> {video: ["is too large (max is 50 MB)"]}
|
395
|
-
```
|
396
|
-
|
397
|
-
You can also do custom validations:
|
398
|
-
|
399
|
-
```rb
|
400
|
-
class VideoUploader < Shrine
|
401
|
-
Attacher.validate do
|
402
|
-
errors << "is longer than 5 minutes" if get.duration > 300
|
459
|
+
process(:store) do |io, context|
|
460
|
+
trim_video(io, 300) if context[:record].guest?
|
403
461
|
end
|
404
462
|
end
|
405
463
|
```
|
406
464
|
|
407
|
-
The
|
408
|
-
|
465
|
+
The context is just a hash that is passed to the uploader methods. If you're
|
466
|
+
using the uploader directly, you can pass the context directly:
|
409
467
|
|
410
468
|
```rb
|
411
|
-
|
412
|
-
Attacher.validate do
|
413
|
-
self #=> #<Shrine::Attacher>
|
414
|
-
|
415
|
-
get #=> #<Shrine::UploadedFile>
|
416
|
-
record # the model instance
|
417
|
-
errors # array of error messages for this file
|
418
|
-
end
|
419
|
-
end
|
469
|
+
uploader.upload(file, {foo: "bar"}) # passing context hash directly
|
420
470
|
```
|
421
471
|
|
422
472
|
## Metadata
|
423
473
|
|
424
|
-
Shrine automatically extracts and stores
|
474
|
+
Shrine automatically extracts and stores available file metadata:
|
425
475
|
|
426
476
|
```rb
|
427
477
|
photo = Photo.create(image: image)
|
@@ -480,6 +530,54 @@ photo.image.metadata["exif"]
|
|
480
530
|
photo.image.exif
|
481
531
|
```
|
482
532
|
|
533
|
+
## Validation
|
534
|
+
|
535
|
+
Validations are registered inside a `Attacher.validate` block, and you can load
|
536
|
+
the `validation_helpers` plugin to get some convenient file validation methods:
|
537
|
+
|
538
|
+
```rb
|
539
|
+
class VideoUploader < Shrine
|
540
|
+
plugin :validation_helpers
|
541
|
+
|
542
|
+
Attacher.validate do
|
543
|
+
validate_max_size 50*1024*1024, message: "is too large (max is 50 MB)"
|
544
|
+
validate_mime_type_inclusion ["video/mp4"]
|
545
|
+
end
|
546
|
+
end
|
547
|
+
```
|
548
|
+
|
549
|
+
```rb
|
550
|
+
trailer = Trailer.new
|
551
|
+
trailer.video = File.open("matrix.mp4")
|
552
|
+
trailer.valid? #=> false
|
553
|
+
trailer.errors.to_hash #=> {video: ["is too large (max is 50 MB)"]}
|
554
|
+
```
|
555
|
+
|
556
|
+
You can also do custom validations:
|
557
|
+
|
558
|
+
```rb
|
559
|
+
class VideoUploader < Shrine
|
560
|
+
Attacher.validate do
|
561
|
+
errors << "is longer than 5 minutes" if get.duration > 300
|
562
|
+
end
|
563
|
+
end
|
564
|
+
```
|
565
|
+
|
566
|
+
The `Attacher.validate` block is executed in context of a `Shrine::Attacher`
|
567
|
+
instance:
|
568
|
+
|
569
|
+
```rb
|
570
|
+
class VideoUploader < Shrine
|
571
|
+
Attacher.validate do
|
572
|
+
self #=> #<Shrine::Attacher>
|
573
|
+
|
574
|
+
get #=> #<Shrine::UploadedFile>
|
575
|
+
record # the model instance
|
576
|
+
errors # array of error messages for this file
|
577
|
+
end
|
578
|
+
end
|
579
|
+
```
|
580
|
+
|
483
581
|
## Locations
|
484
582
|
|
485
583
|
Before Shrine uploads a file, it generates a random location for it. By
|
@@ -568,11 +666,22 @@ uploader = MyUploader.new(:store)
|
|
568
666
|
uploader.upload(file, upload_options: {acl: "private"})
|
569
667
|
```
|
570
668
|
|
669
|
+
### Clearing cache
|
670
|
+
|
671
|
+
From time to time you'll want to clean your temporary storage from old files.
|
672
|
+
Amazon S3 provides [a built-in solution][s3 lifecycle], and for FileSystem you
|
673
|
+
can put something like this in your Rake task:
|
674
|
+
|
675
|
+
```rb
|
676
|
+
file_system = Shrine.storages[:cache]
|
677
|
+
file_system.clear!(older_than: Time.now - 7*24*60*60) # delete files older than 1 week
|
678
|
+
```
|
679
|
+
|
571
680
|
## Direct uploads
|
572
681
|
|
573
|
-
Shrine comes with a `direct_upload` plugin for asynchronous uploads to your
|
574
|
-
|
575
|
-
in your app:
|
682
|
+
Shrine comes with a `direct_upload` plugin for asynchronous uploads to your app
|
683
|
+
or an external service like Amazon S3. It provides a [Roda] endpoint which you
|
684
|
+
can mount in your app:
|
576
685
|
|
577
686
|
```rb
|
578
687
|
gem "roda"
|
@@ -589,7 +698,7 @@ end
|
|
589
698
|
This endpoint provides the following routes:
|
590
699
|
|
591
700
|
* `POST /images/cache/upload` - for direct uploads to your app
|
592
|
-
* `GET /images/cache/presign` - for direct uploads to external service
|
701
|
+
* `GET /images/cache/presign` - for direct uploads to external service (e.g. Amazon S3)
|
593
702
|
|
594
703
|
These routes can be used to asynchronously start caching the file the moment
|
595
704
|
the user selects it, using JavaScript file upload libraries like
|
@@ -647,17 +756,6 @@ libraries are:
|
|
647
756
|
* **Safety** – All of Shrine's code has been designed to take delayed storing
|
648
757
|
into account, and concurrent requests are handled well.
|
649
758
|
|
650
|
-
## Clearing cache
|
651
|
-
|
652
|
-
From time to time you'll want to clean your temporary storage from old files.
|
653
|
-
Amazon S3 provides [a built-in solution](http://docs.aws.amazon.com/AmazonS3/latest/UG/lifecycle-configuration-bucket-no-versioning.html),
|
654
|
-
and for FileSystem you can put something like this in your Rake task:
|
655
|
-
|
656
|
-
```rb
|
657
|
-
file_system = Shrine.storages[:cache]
|
658
|
-
file_system.clear!(older_than: Time.now - 7*24*60*60) # delete files older than 1 week
|
659
|
-
```
|
660
|
-
|
661
759
|
## On-the-fly processing
|
662
760
|
|
663
761
|
Shrine allows you to define processing that will be performed on upload.
|
@@ -669,9 +767,26 @@ generic image servers.
|
|
669
767
|
Shrine has integrations for many commercial on-the-fly processing services,
|
670
768
|
including [Cloudinary], [Imgix] and [Uploadcare].
|
671
769
|
|
672
|
-
If you don't want to use a commercial service, [Attache]
|
673
|
-
image
|
674
|
-
|
770
|
+
If you don't want to use a commercial service, [Attache] and [Dragonfly] are
|
771
|
+
great open-source image servers. For Attache a Shrine integration is in
|
772
|
+
progress, while for Dragonfly it is not needed.
|
773
|
+
|
774
|
+
## Chunked & Resumable uploads
|
775
|
+
|
776
|
+
When you're accepting large file uploads, you normally want to split it into
|
777
|
+
multiple chunks. This way if an upload fails, it is just for one chunk and can
|
778
|
+
be retried, while the previous chunks remain uploaded.
|
779
|
+
|
780
|
+
[Tus][tus] is an open protocol for resumable file uploads, which enables the
|
781
|
+
client and the server to achieve reliable file uploads, even on unstable
|
782
|
+
networks, with the possibility to resume the upload even after the browser is
|
783
|
+
closed or the device shut down. You can use a client library like
|
784
|
+
[tus-js-client] to upload the file to [tus-ruby-server], and attach the
|
785
|
+
uploaded file to a record using [shrine-url]. See [shrine-tus-demo] for an
|
786
|
+
example integration.
|
787
|
+
|
788
|
+
Another option might be to do chunked uploads directly to your storage service,
|
789
|
+
if the storage service supports it (e.g. Amazon S3 or Google Cloud Storage).
|
675
790
|
|
676
791
|
## Inspiration
|
677
792
|
|
@@ -704,7 +819,7 @@ The gem is available as open source under the terms of the [MIT License].
|
|
704
819
|
[plugin system]: http://twin.github.io/the-plugin-system-of-sequel-and-roda/
|
705
820
|
[MIT License]: http://opensource.org/licenses/MIT
|
706
821
|
[ships with over 35 plugins]: http://shrinerb.com#plugins
|
707
|
-
[
|
822
|
+
[motivation]: https://twin.github.io/better-file-uploads-with-shrine-motivation/
|
708
823
|
[FileSystem]: http://shrinerb.com/rdoc/classes/Shrine/Storage/FileSystem.html
|
709
824
|
[S3]: http://shrinerb.com/rdoc/classes/Shrine/Storage/S3.html
|
710
825
|
[External]: http://shrinerb.com#external
|
@@ -721,3 +836,11 @@ The gem is available as open source under the terms of the [MIT License].
|
|
721
836
|
[Direct Uploads to S3]: http://shrinerb.com/rdoc/files/doc/direct_s3_md.html
|
722
837
|
[website]: http://shrinerb.com
|
723
838
|
[backgrounding libraries]: https://github.com/janko-m/shrine/wiki/Backgrounding-libraries
|
839
|
+
[tus]: http://tus.io
|
840
|
+
[tus-ruby-server]: https://github.com/janko-m/tus-ruby-server
|
841
|
+
[tus-js-client]: https://github.com/tus/tus-js-client
|
842
|
+
[shrine-tus-demo]: https://github.com/janko-m/shrine-tus-demo
|
843
|
+
[shrine-url]: https://github.com/janko-m/shrine-url
|
844
|
+
[s3 lifecycle]: http://docs.aws.amazon.com/AmazonS3/latest/UG/lifecycle-configuration-bucket-no-versioning.html
|
845
|
+
[Dragonfly]: http://markevans.github.io/dragonfly/
|
846
|
+
[Using Attacher]: http://shrinerb.com/rdoc/files/doc/attacher_md.html
|