shrine 3.0.0 → 3.2.2
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.
Potentially problematic release.
This version of shrine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +87 -33
- data/LICENSE.txt +1 -1
- data/README.md +94 -4
- data/doc/advantages.md +35 -18
- data/doc/attacher.md +16 -17
- data/doc/carrierwave.md +75 -34
- data/doc/changing_derivatives.md +39 -39
- data/doc/design.md +134 -85
- data/doc/external/articles.md +56 -41
- data/doc/external/extensions.md +38 -34
- data/doc/getting_started.md +182 -112
- data/doc/metadata.md +79 -43
- data/doc/multiple_files.md +5 -3
- data/doc/paperclip.md +110 -42
- data/doc/plugins/activerecord.md +5 -5
- data/doc/plugins/add_metadata.md +92 -35
- data/doc/plugins/backgrounding.md +12 -2
- data/doc/plugins/column.md +36 -7
- data/doc/plugins/data_uri.md +2 -2
- data/doc/plugins/default_url.md +6 -3
- data/doc/plugins/derivation_endpoint.md +26 -28
- data/doc/plugins/derivatives.md +205 -169
- data/doc/plugins/determine_mime_type.md +2 -2
- data/doc/plugins/entity.md +3 -3
- data/doc/plugins/form_assign.md +5 -5
- data/doc/plugins/included.md +25 -5
- data/doc/plugins/infer_extension.md +2 -2
- data/doc/plugins/instrumentation.md +1 -1
- data/doc/plugins/metadata_attributes.md +21 -10
- data/doc/plugins/model.md +4 -4
- data/doc/plugins/persistence.md +1 -0
- data/doc/plugins/refresh_metadata.md +5 -4
- data/doc/plugins/remote_url.md +8 -3
- data/doc/plugins/remove_invalid.md +9 -1
- data/doc/plugins/sequel.md +4 -4
- data/doc/plugins/signature.md +11 -2
- data/doc/plugins/store_dimensions.md +2 -2
- data/doc/plugins/type_predicates.md +96 -0
- data/doc/plugins/upload_endpoint.md +7 -11
- data/doc/plugins/upload_options.md +1 -1
- data/doc/plugins/url_options.md +2 -2
- data/doc/plugins/validation.md +14 -4
- data/doc/plugins/validation_helpers.md +3 -3
- data/doc/plugins/versions.md +11 -11
- data/doc/processing.md +289 -125
- data/doc/refile.md +39 -18
- data/doc/release_notes/2.19.0.md +1 -1
- data/doc/release_notes/3.0.0.md +275 -258
- data/doc/release_notes/3.0.1.md +22 -0
- data/doc/release_notes/3.1.0.md +73 -0
- data/doc/release_notes/3.2.0.md +96 -0
- data/doc/release_notes/3.2.1.md +32 -0
- data/doc/release_notes/3.2.2.md +14 -0
- data/doc/securing_uploads.md +3 -3
- data/doc/storage/file_system.md +1 -1
- data/doc/storage/memory.md +19 -0
- data/doc/storage/s3.md +105 -86
- data/doc/testing.md +2 -2
- data/doc/upgrading_to_3.md +115 -33
- data/doc/validation.md +3 -2
- data/lib/shrine.rb +8 -8
- data/lib/shrine/attacher.rb +19 -14
- data/lib/shrine/attachment.rb +5 -5
- data/lib/shrine/plugins.rb +22 -0
- data/lib/shrine/plugins/add_metadata.rb +12 -3
- data/lib/shrine/plugins/default_storage.rb +6 -6
- data/lib/shrine/plugins/default_url.rb +1 -1
- data/lib/shrine/plugins/derivation_endpoint.rb +10 -6
- data/lib/shrine/plugins/derivatives.rb +19 -17
- data/lib/shrine/plugins/determine_mime_type.rb +3 -3
- data/lib/shrine/plugins/entity.rb +6 -6
- data/lib/shrine/plugins/metadata_attributes.rb +1 -1
- data/lib/shrine/plugins/model.rb +3 -3
- data/lib/shrine/plugins/presign_endpoint.rb +2 -2
- data/lib/shrine/plugins/pretty_location.rb +1 -1
- data/lib/shrine/plugins/processing.rb +1 -1
- data/lib/shrine/plugins/refresh_metadata.rb +2 -2
- data/lib/shrine/plugins/remote_url.rb +3 -3
- data/lib/shrine/plugins/remove_invalid.rb +10 -5
- data/lib/shrine/plugins/signature.rb +7 -6
- data/lib/shrine/plugins/store_dimensions.rb +18 -9
- data/lib/shrine/plugins/type_predicates.rb +113 -0
- data/lib/shrine/plugins/upload_endpoint.rb +3 -3
- data/lib/shrine/plugins/upload_options.rb +2 -2
- data/lib/shrine/plugins/url_options.rb +2 -2
- data/lib/shrine/plugins/validation.rb +9 -7
- data/lib/shrine/storage/linter.rb +4 -4
- data/lib/shrine/storage/s3.rb +62 -38
- data/lib/shrine/uploaded_file.rb +5 -1
- data/lib/shrine/version.rb +2 -2
- data/shrine.gemspec +6 -7
- metadata +23 -29
data/doc/external/extensions.md
CHANGED
@@ -4,43 +4,47 @@ title: Extensions
|
|
4
4
|
|
5
5
|
## Storages
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
* [shrine-storage-you_tube](https://github.com/thedyrt/shrine-storage-you_tube)
|
25
|
-
* [shrine-webdav](https://github.com/funbox/shrine-webdav)
|
7
|
+
| Gem | Description |
|
8
|
+
| :---- | :---------- |
|
9
|
+
| [shrine-aliyun-oss](https://github.com/zillou/shrine-aliyun-oss) | Storage using [Alibaba Cloud OSS](https://www.alibabacloud.com/product/oss) |
|
10
|
+
| [shrine-cloudinary](https://github.com/shrinerb/shrine-cloudinary) | Storage using [Cloudinary](https://cloudinary.com/) |
|
11
|
+
| [shrine-flickr](https://github.com/shrinerb/shrine-flickr) | Storage using [Flickr](https://flickr.com/) |
|
12
|
+
| [shrine-fog](https://github.com/shrinerb/shrine-fog) | Storage using [Fog](http://fog.io/) |
|
13
|
+
| [shrine-ftp](https://github.com/ProjectResound/shrine-ftp) | Storage using an FTP server |
|
14
|
+
| [shrine-google_cloud_storage](https://github.com/renchap/shrine-google_cloud_storage) | Storage using [Google Cloud Storage](https://cloud.google.com/storage/) |
|
15
|
+
| [shrine-gdrive_storage](https://github.com/edwardsharp/shrine-gdrive_storage) | Storage using [Google Drive](https://www.google.com/drive/) |
|
16
|
+
| [shrine-gridfs](https://github.com/shrinerb/shrine-gridfs) | Storage using [Mongo GridFS](https://docs.mongodb.com/manual/core/gridfs/) |
|
17
|
+
| [shrine-redis](https://github.com/dbongo/shrine-redis) | Storage using [Redis](https://redis.io/) |
|
18
|
+
| [shrine-scp](https://github.com/jordanandree/shrine-scp) | Storage using `scp` |
|
19
|
+
| [shrine-sql](https://github.com/shrinerb/shrine-sql) | Storage using an SQL database |
|
20
|
+
| [shrine-uploadcare](https://github.com/shrinerb/shrine-uploadcare) | Storage using [Uploadcare](https://uploadcare.com) |
|
21
|
+
| [shrine-url](https://github.com/shrinerb/shrine-url) | Storage for handling remote URLs |
|
22
|
+
| [shrine-storage-you_tube](https://github.com/thedyrt/shrine-storage-you_tube) | Storage using [YouTube](https://www.youtube.com/) |
|
23
|
+
| [shrine-webdav](https://github.com/funbox/shrine-webdav) | Storage using a [WebDAV](https://en.wikipedia.org/wiki/WebDAV) server |
|
26
24
|
|
27
25
|
## Plugins
|
28
26
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
27
|
+
| Gem | Description |
|
28
|
+
| :---- | :-------- |
|
29
|
+
| [administrate-field-shrine](https://github.com/catsky/administrate-field-shrine) | Plugin for [Administrate](https://github.com/thoughtbot/administrate) |
|
30
|
+
| [rails_admin_shrine](https://github.com/iquest/rails_admin_shrine) | Plugin for [RailsAdmin](https://github.com/sferik/rails_admin) |
|
31
|
+
| [shrine-color](https://github.com/jnylen/shrine-color) | Plugin for finding dominant color in an image |
|
32
|
+
| [shrine-configurable_storage](https://github.com/SleeplessByte/shrine-configurable_storage) | Plugin for lazy storage registration |
|
33
|
+
| [shrine-content_addressable](https://github.com/SleeplessByte/shrine-content_addressable) | Plugin for generating content addressable locations |
|
34
|
+
| [shrine-imgix](https://github.com/shrinerb/shrine-imgix) | Plugin for [Imgix](https://www.imgix.com/) |
|
35
|
+
| [shrine-transloadit](https://github.com/shrinerb/shrine-transloadit) | Plugin for [Transloadit](https://transloadit.com/) |
|
36
|
+
| [shrine-lambda](https://github.com/texpert/shrine-lambda) | Plugin for [AWS Lambda](https://aws.amazon.com/lambda/) |
|
37
|
+
| [hanami-shrine](https://github.com/katafrakt/hanami-shrine) | Plugin for [Hanami](https://hanamirb.org/) |
|
38
|
+
| [shrine-mongoid](https://github.com/shrinerb/shrine-mongoid) | Plugin for [Mongoid](https://mongoid.org) |
|
39
|
+
| [shrine-rails](https://github.com/abepetrillo/shrine-rails) | Plugin for [Rails](https://rubyonrails.org/) |
|
40
|
+
| [shrine-rom](https://github.com/shrinerb/shrine-rom) | Plugin for [ROM](https://rom-rb.org/) |
|
41
|
+
| [shrine-tus](https://github.com/shrinerb/shrine-tus) | Plugin for [tus](https://tus.io) server integration |
|
40
42
|
|
41
43
|
## Libraries
|
42
44
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
45
|
+
| Gem | Description |
|
46
|
+
| :----- | :------- |
|
47
|
+
| [ckeditor](https://github.com/galetahub/ckeditor) | Integration for [CKEditor](https://ckeditor.com/ckeditor-4/) |
|
48
|
+
| [imgproxy](https://github.com/imgproxy/imgproxy.rb) | Integration for [imgproxy](https://github.com/imgproxy/imgproxy) |
|
49
|
+
| [rails_admin](https://github.com/sferik/rails_admin) | Integration for [RailsAdmin](https://github.com/sferik/rails_admin) |
|
50
|
+
| [uppy-s3_multipart](https://github.com/janko/uppy-s3_multipart) | Integration for [Uppy AWS S3 Multipart](https://uppy.io/docs/aws-s3-multipart/) |
|
data/doc/getting_started.md
CHANGED
@@ -50,7 +50,7 @@ class AddImageDataToPhotos < ActiveRecord::Migration
|
|
50
50
|
end
|
51
51
|
```
|
52
52
|
<!--Rails-->
|
53
|
-
```
|
53
|
+
```
|
54
54
|
$ rails generate migration add_image_data_to_photos image_data:text
|
55
55
|
```
|
56
56
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
@@ -110,6 +110,14 @@ form @photo, action: "/photos", enctype: "multipart/form-data" do |f|
|
|
110
110
|
f.button "Create"
|
111
111
|
end
|
112
112
|
```
|
113
|
+
<!--HTML-->
|
114
|
+
```erb
|
115
|
+
<form action="/photos" method="post" enctype="multipart/form-data">
|
116
|
+
<input name="photo[image]" type="hidden" value="<%= @photo.cached_image_data %>" />
|
117
|
+
<input name="photo[image] "type="file" />
|
118
|
+
<input type="submit" value="Create" />
|
119
|
+
</form>
|
120
|
+
```
|
113
121
|
<!--END_DOCUSAURUS_CODE_TABS-->
|
114
122
|
|
115
123
|
Note that the file field needs to go *after* the hidden field, so that
|
@@ -167,9 +175,13 @@ specific storage service, by implementing a common public interface. Storage
|
|
167
175
|
instances are registered under an identifier in `Shrine.storages`, so that they
|
168
176
|
can later be used by [uploaders][uploader].
|
169
177
|
|
170
|
-
|
171
|
-
|
172
|
-
|
178
|
+
Shrine ships with the following storages:
|
179
|
+
|
180
|
+
* [`Shrine::Storage::FileSystem`][FileSystem] – stores files on disk
|
181
|
+
* [`Shrine::Storage::S3`][S3] – stores files on [AWS S3] (or [DigitalOcean Spaces], [MinIO], ...)
|
182
|
+
* [`Shrine::Storage::Memory`][Memory] – stores file in memory (convenient for [testing][Testing with Shrine])
|
183
|
+
|
184
|
+
Here is how we might configure Shrine with S3 storage:
|
173
185
|
|
174
186
|
```rb
|
175
187
|
# Gemfile
|
@@ -180,14 +192,14 @@ require "shrine/storage/s3"
|
|
180
192
|
|
181
193
|
s3_options = {
|
182
194
|
bucket: "<YOUR BUCKET>", # required
|
195
|
+
region: "<YOUR REGION>", # required
|
183
196
|
access_key_id: "<YOUR ACCESS KEY ID>",
|
184
197
|
secret_access_key: "<YOUR SECRET ACCESS KEY>",
|
185
|
-
region: "<YOUR REGION>",
|
186
198
|
}
|
187
199
|
|
188
200
|
Shrine.storages = {
|
189
|
-
cache: Shrine::Storage::S3.new(prefix: "cache", **s3_options),
|
190
|
-
store: Shrine::Storage::S3.new(**s3_options),
|
201
|
+
cache: Shrine::Storage::S3.new(prefix: "cache", **s3_options), # temporary
|
202
|
+
store: Shrine::Storage::S3.new(**s3_options), # permanent
|
191
203
|
}
|
192
204
|
```
|
193
205
|
|
@@ -196,9 +208,9 @@ suitable for [direct uploads][presigned upload]. The `:cache` and `:store`
|
|
196
208
|
names are special only in terms that the [attacher] will automatically pick
|
197
209
|
them up, you can also register more storage objects under different names.
|
198
210
|
|
199
|
-
See the [FileSystem]
|
200
|
-
more Shrine storages][storages] provided by external gems, and you can
|
201
|
-
[create your own storage][Creating Storages].
|
211
|
+
See the [FileSystem]/[S3]/[Memory] storage docs for more details. There are
|
212
|
+
[many more Shrine storages][storages] provided by external gems, and you can
|
213
|
+
also [create your own storage][Creating Storages].
|
202
214
|
|
203
215
|
## Uploader
|
204
216
|
|
@@ -254,15 +266,17 @@ uploader.upload(io, upload_options: { acl: "public-read" }) # add options to Sto
|
|
254
266
|
|
255
267
|
Shrine is able to upload any IO-like object that implement methods [`#read`],
|
256
268
|
[`#rewind`], [`#eof?`] and [`#close`] whose behaviour matches the [`IO`] class.
|
257
|
-
This includes
|
269
|
+
This includes but is not limited to the following objects:
|
258
270
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
271
|
+
* [`File`](https://ruby-doc.org/core/File.html)
|
272
|
+
* [`Tempfile`](https://ruby-doc.org/stdlib/libdoc/tempfile/rdoc/Tempfile.html)
|
273
|
+
* [`StringIO`](https://ruby-doc.org/stdlib/libdoc/stringio/rdoc/StringIO.html)
|
274
|
+
* [`ActionDispatch::Http::UploadedFile`](https://api.rubyonrails.org/classes/ActionDispatch/Http/UploadedFile.html)
|
275
|
+
* [`Shrine::RackFile`](https://shrinerb.com/docs/plugins/rack_file)
|
276
|
+
* [`Shrine::DataFile`](https://shrinerb.com/docs/plugins/data_uri)
|
277
|
+
* [`Shrine::UploadedFile`](#uploaded-file)
|
278
|
+
* [`Down::ChunkedIO`](https://github.com/janko/down#streaming)
|
279
|
+
* ...
|
266
280
|
|
267
281
|
```rb
|
268
282
|
uploader.upload File.open("/path/to/file", binmode: true) # upload from disk
|
@@ -278,7 +292,15 @@ uploader.upload Shrine::UploadedFile.new(...) # upload from Shrine
|
|
278
292
|
|
279
293
|
The `Shrine::UploadedFile` object represents the file that was uploaded to a
|
280
294
|
storage, and it's what's returned from `Shrine#upload` or when retrieving a
|
281
|
-
record [attachment].
|
295
|
+
record [attachment].
|
296
|
+
|
297
|
+
```rb
|
298
|
+
uploader.upload(file) #=> #<Shrine::UploadedFile ...> (uploader)
|
299
|
+
photo.image #=> #<Shrine::UploadedFile ...> (attachment)
|
300
|
+
attacher.file #=> #<Shrine::UploadedFile ...> (attacher)
|
301
|
+
```
|
302
|
+
|
303
|
+
An uploaded file object contains the following data:
|
282
304
|
|
283
305
|
| Key | Description |
|
284
306
|
| :------- | :---------- |
|
@@ -287,12 +309,12 @@ record [attachment]. It contains the following information:
|
|
287
309
|
| `metadata` | file [metadata] that was extracted before upload |
|
288
310
|
|
289
311
|
```rb
|
290
|
-
uploaded_file =
|
291
|
-
uploaded_file.data #=> {"id"=>"949sdjg834.jpg","storage"=>"store","metadata"=>{...}}
|
312
|
+
uploaded_file #=> #<Shrine::UploadedFile id="949sdjg834.jpg" storage=:store metadata={...}>
|
292
313
|
|
293
|
-
uploaded_file.id
|
294
|
-
uploaded_file.
|
295
|
-
uploaded_file.
|
314
|
+
uploaded_file.id #=> "949sdjg834.jpg"
|
315
|
+
uploaded_file.storage_key #=> :store
|
316
|
+
uploaded_file.storage #=> #<Shrine::Storage::S3>
|
317
|
+
uploaded_file.metadata #=> {...}
|
296
318
|
```
|
297
319
|
|
298
320
|
It comes with many convenient methods that delegate to the storage:
|
@@ -340,7 +362,7 @@ The easiest way to attach files is with the `Shrine::Attachment` module:
|
|
340
362
|
```rb
|
341
363
|
class Photo < Sequel::Model # ActiveRecord::Base
|
342
364
|
include ImageUploader::Attachment.new(:image) #
|
343
|
-
include ImageUploader::Attachment[:image] # use
|
365
|
+
include ImageUploader::Attachment[:image] # use your preferred syntax
|
344
366
|
include ImageUploader::Attachment(:image) #
|
345
367
|
end
|
346
368
|
```
|
@@ -363,14 +385,14 @@ that attachments are deleted when the record is destroyed.
|
|
363
385
|
photo.image #=> nil
|
364
386
|
|
365
387
|
# the assigned file is cached to temporary storage and written to `image_data` column
|
366
|
-
photo.image = File.open("waterfall.jpg")
|
367
|
-
photo.image #=> #<Shrine::UploadedFile
|
388
|
+
photo.image = File.open("waterfall.jpg", "rb")
|
389
|
+
photo.image #=> #<Shrine::UploadedFile ...>
|
368
390
|
photo.image_url #=> "/uploads/cache/0sdfllasfi842.jpg"
|
369
391
|
photo.image_data #=> '{"id":"0sdfllasfi842.jpg","storage":"cache","metadata":{...}}'
|
370
392
|
|
371
393
|
# the cached file is promoted to permanent storage and saved to `image_data` column
|
372
394
|
photo.save
|
373
|
-
photo.image #=> #<Shrine::UploadedFile
|
395
|
+
photo.image #=> #<Shrine::UploadedFile ...>
|
374
396
|
photo.image_url #=> "/uploads/store/l02kladf8jlda.jpg"
|
375
397
|
photo.image_data #=> '{"id":"l02kladf8jlda.jpg","storage":"store","metadata":{...}}'
|
376
398
|
|
@@ -407,38 +429,36 @@ attacher.url # equivalent to `photo.image_url`
|
|
407
429
|
```
|
408
430
|
|
409
431
|
The attacher is what drives attaching files to model instances; you can use it
|
410
|
-
as a more explicit alternative to models' attachment interface, or
|
411
|
-
|
432
|
+
as a more explicit alternative to models' attachment interface, or when you
|
433
|
+
need something that's not available through the attachment methods.
|
412
434
|
|
413
|
-
|
414
|
-
attacher uses, or upload files directly to permanent storage. See the [Using
|
415
|
-
Attacher] guide for more details.
|
435
|
+
See [Using Attacher] guide for more details.
|
416
436
|
|
417
437
|
### Temporary storage
|
418
438
|
|
419
|
-
Shrine uses temporary storage to support
|
420
|
-
|
421
|
-
files
|
439
|
+
Shrine uses temporary storage to support [file validation][validation] and
|
440
|
+
[direct uploads]. If you don't need these features, you can tell Shrine to
|
441
|
+
upload files directly to permanent storage:
|
422
442
|
|
423
443
|
```rb
|
424
444
|
Shrine.plugin :model, cache: false
|
425
445
|
```
|
426
|
-
<!--DOCUSAURUS_CODE_TABS-->
|
427
|
-
<!--Attachment-->
|
428
446
|
```rb
|
429
|
-
photo.image = File.open("waterfall.jpg")
|
447
|
+
photo.image = File.open("waterfall.jpg", "rb")
|
430
448
|
photo.image.storage_key #=> :store
|
431
449
|
```
|
432
|
-
|
450
|
+
|
451
|
+
If you're using the attacher directly, you can just use `Attacher#attach`
|
452
|
+
instead of `Attacher#assign`:
|
453
|
+
|
433
454
|
```rb
|
434
|
-
attacher.attach File.open("waterfall.jpg")
|
455
|
+
attacher.attach File.open("waterfall.jpg", "rb")
|
435
456
|
attacher.file.storage_key #=> :store
|
436
457
|
```
|
437
|
-
<!--END_DOCUSAURUS_CODE_TABS-->
|
438
458
|
|
439
459
|
## Plugin system
|
440
460
|
|
441
|
-
By default Shrine comes with a small core which provides only the essential
|
461
|
+
By default, Shrine comes with a small core which provides only the essential
|
442
462
|
functionality. All additional features are available via [plugins], which also
|
443
463
|
ship with Shrine. This way you can choose exactly what and how much Shrine does
|
444
464
|
for you, and you load the code only for features that you use.
|
@@ -496,7 +516,7 @@ gem "marcel", "~> 0.3"
|
|
496
516
|
Shrine.plugin :determine_mime_type, analyzer: :marcel
|
497
517
|
```
|
498
518
|
```rb
|
499
|
-
photo = Photo.
|
519
|
+
photo = Photo.new(image: StringIO.new("<?php ... ?>"))
|
500
520
|
photo.image.mime_type #=> "application/x-php"
|
501
521
|
```
|
502
522
|
|
@@ -509,23 +529,25 @@ the [Extracting Metadata] guide for more details.
|
|
509
529
|
|
510
530
|
## Processing
|
511
531
|
|
512
|
-
Shrine allows you to process attached files
|
513
|
-
example, if your app is accepting image uploads, you can generate a
|
514
|
-
set of of thumbnails when the image is attached to a record, or you
|
515
|
-
thumbnails generated dynamically as they're needed.
|
532
|
+
Shrine allows you to process attached files both "eagerly" and "on-the-fly".
|
533
|
+
For example, if your app is accepting image uploads, you can generate a
|
534
|
+
predefined set of of thumbnails when the image is attached to a record, or you
|
535
|
+
can have thumbnails generated dynamically as they're needed.
|
516
536
|
|
517
537
|
For image processing, it's recommended to use the **[ImageProcessing]** gem,
|
518
538
|
which is a high-level wrapper for processing with
|
519
539
|
[MiniMagick][ImageProcessing::MiniMagick] and [libvips][ImageProcessing::Vips].
|
520
540
|
|
521
|
-
```
|
541
|
+
```
|
522
542
|
$ brew install imagemagick vips
|
523
543
|
```
|
524
544
|
|
525
|
-
###
|
545
|
+
### Eager processing
|
526
546
|
|
527
|
-
|
528
|
-
pre-defined processed files
|
547
|
+
We can use the [`derivatives`][derivatives plugin] plugin to generate a
|
548
|
+
pre-defined set of processed files (e.g. image thumbnails). We do this by
|
549
|
+
registering a derivatives processor block and then explicitly triggering
|
550
|
+
creation:
|
529
551
|
|
530
552
|
```rb
|
531
553
|
# Gemfile
|
@@ -538,7 +560,7 @@ Shrine.plugin :derivatives
|
|
538
560
|
require "image_processing/mini_magick"
|
539
561
|
|
540
562
|
class ImageUploader < Shrine
|
541
|
-
Attacher.
|
563
|
+
Attacher.derivatives do |original|
|
542
564
|
magick = ImageProcessing::MiniMagick.source(original)
|
543
565
|
|
544
566
|
{
|
@@ -551,40 +573,38 @@ end
|
|
551
573
|
```
|
552
574
|
```rb
|
553
575
|
photo = Photo.new(image: file)
|
554
|
-
|
555
|
-
photo.
|
576
|
+
|
577
|
+
if photo.valid?
|
578
|
+
photo.image_derivatives! if photo.image_changed? # create derivatives
|
579
|
+
photo.save
|
580
|
+
end
|
556
581
|
```
|
557
582
|
|
558
|
-
|
559
|
-
route make sure to trigger derivatives creation for new attachments:
|
583
|
+
You can then retrieve the URL of a processed derivative:
|
560
584
|
|
561
585
|
```rb
|
562
|
-
photo.
|
586
|
+
photo.image_url(:large) #=> "https://s3.amazonaws.com/path/to/large.jpg"
|
563
587
|
```
|
564
588
|
|
565
|
-
|
566
|
-
|
567
|
-
[`Shrine::UploadedFile`][uploaded file] objects:
|
589
|
+
The derivatives data is stored in the `<attachment>_data` column, and you can
|
590
|
+
retrieve them as [`Shrine::UploadedFile`][uploaded file] objects:
|
568
591
|
|
569
592
|
```rb
|
570
|
-
photo.image(:large)
|
571
|
-
photo.image(:large).url
|
572
|
-
photo.image(:large).size
|
573
|
-
photo.image(:large).mime_type
|
593
|
+
photo.image(:large) #=> #<Shrine::UploadedFile id="path/to/large.jpg" storage=:store metadata={...}>
|
594
|
+
photo.image(:large).url #=> "https://s3.amazonaws.com/path/to/large.jpg"
|
595
|
+
photo.image(:large).size #=> 5825949
|
596
|
+
photo.image(:large).mime_type #=> "image/jpeg"
|
574
597
|
```
|
575
598
|
|
576
|
-
For more details, see the [
|
577
|
-
|
599
|
+
For more details, see the [File Processing] guide and the
|
600
|
+
[`derivatives`][derivatives plugin] plugin documentation.
|
578
601
|
|
579
|
-
###
|
602
|
+
### On-the-fly processing
|
580
603
|
|
581
604
|
On-the-fly processing is provided by the
|
582
|
-
[`derivation_endpoint`][derivation_endpoint plugin] plugin.
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
To set it up, we mount the Rack app in our router on a chosen path prefix,
|
587
|
-
configure the plugin with a secret key and that path prefix, and define
|
605
|
+
[`derivation_endpoint`][derivation_endpoint plugin] plugin. To set it up, we
|
606
|
+
configure the plugin with a secret key and a path prefix, [mount][Mounting
|
607
|
+
Endpoints] its Rack app in our routes on the configured path prefix, and define
|
588
608
|
processing we want to perform:
|
589
609
|
|
590
610
|
```rb
|
@@ -592,19 +612,15 @@ processing we want to perform:
|
|
592
612
|
gem "image_processing", "~> 1.8"
|
593
613
|
```
|
594
614
|
```rb
|
595
|
-
# config/
|
596
|
-
|
597
|
-
|
598
|
-
mount ImageUploader.derivation_endpoint => "/derivations/image"
|
599
|
-
end
|
615
|
+
# config/initializers/rails.rb (Rails)
|
616
|
+
# ...
|
617
|
+
Shrine.plugin :derivation_endpoint, secret_key: "<YOUR_SECRET_KEY>"
|
600
618
|
```
|
601
619
|
```rb
|
602
620
|
require "image_processing/mini_magick"
|
603
621
|
|
604
622
|
class ImageUploader < Shrine
|
605
|
-
plugin :derivation_endpoint,
|
606
|
-
secret_key: "<YOUR SECRET KEY>",
|
607
|
-
prefix: "derivations/image" # needs to match the mount point in routes
|
623
|
+
plugin :derivation_endpoint, prefix: "derivations/image" # matches mount point
|
608
624
|
|
609
625
|
derivation :thumbnail do |file, width, height|
|
610
626
|
ImageProcessing::MiniMagick
|
@@ -613,6 +629,13 @@ class ImageUploader < Shrine
|
|
613
629
|
end
|
614
630
|
end
|
615
631
|
```
|
632
|
+
```rb
|
633
|
+
# config/routes.rb (Rails)
|
634
|
+
Rails.application.routes.draw do
|
635
|
+
# ...
|
636
|
+
mount ImageUploader.derivation_endpoint => "/derivations/image"
|
637
|
+
end
|
638
|
+
```
|
616
639
|
|
617
640
|
Now we can generate URLs from attached files that will perform the desired
|
618
641
|
processing:
|
@@ -657,36 +680,85 @@ For more details, see the [File Validation] guide and
|
|
657
680
|
|
658
681
|
## Location
|
659
682
|
|
660
|
-
Shrine automatically
|
661
|
-
default the hierarchy is flat, meaning all files are stored in the root
|
662
|
-
directory of the storage.
|
663
|
-
|
664
|
-
|
683
|
+
Shrine automatically generates random locations before uploading files. By
|
684
|
+
default, the hierarchy is flat, meaning all files are stored in the root
|
685
|
+
directory of the storage.
|
686
|
+
|
687
|
+
```
|
688
|
+
024d9fe83bf4fafb.jpg
|
689
|
+
768a336bf54de219.jpg
|
690
|
+
adfaa363629f7fc5.png
|
691
|
+
...
|
692
|
+
```
|
693
|
+
|
694
|
+
The [`pretty_location`][pretty_location plugin] plugin provides a good default
|
695
|
+
hierarchy:
|
696
|
+
|
697
|
+
```rb
|
698
|
+
Shrine.plugin :pretty_location
|
699
|
+
```
|
700
|
+
```
|
701
|
+
user/
|
702
|
+
564/
|
703
|
+
avatar/
|
704
|
+
aa3e0cd715.jpg
|
705
|
+
thumb-493g82jf23.jpg
|
706
|
+
photo/
|
707
|
+
123/
|
708
|
+
image/
|
709
|
+
13f8a7bc18.png
|
710
|
+
thumb-9be62da67e.png
|
711
|
+
...
|
712
|
+
```
|
713
|
+
|
714
|
+
Buy you can also override `Shrine#generate_location` with a custom
|
715
|
+
implementation, for example:
|
665
716
|
|
666
717
|
```rb
|
667
718
|
class ImageUploader < Shrine
|
668
719
|
def generate_location(io, record: nil, derivative: nil, **)
|
669
|
-
|
670
|
-
|
671
|
-
|
720
|
+
return super unless record
|
721
|
+
|
722
|
+
table = record.class.table_name
|
723
|
+
id = record.id
|
724
|
+
prefix = derivative || "original"
|
672
725
|
|
673
|
-
|
726
|
+
"uploads/#{table}/#{id}/#{prefix}-#{super}"
|
674
727
|
end
|
675
728
|
end
|
676
729
|
```
|
677
|
-
```
|
730
|
+
```
|
678
731
|
uploads/
|
679
732
|
photos/
|
680
|
-
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
733
|
+
123/
|
734
|
+
original-afe929b8b4.jpg
|
735
|
+
small-ad61f25883.jpg
|
736
|
+
medium-41b75c42bb.jpg
|
737
|
+
large-73e67abe50.jpg
|
738
|
+
...
|
685
739
|
```
|
686
740
|
|
687
|
-
|
688
|
-
|
689
|
-
|
741
|
+
> There should always be a random component in the location, so that the ORM
|
742
|
+
dirty tracking is detected properly.
|
743
|
+
|
744
|
+
The `Shrine#generate_location` method contains a lot of useful context for the
|
745
|
+
upcoming upload:
|
746
|
+
|
747
|
+
```rb
|
748
|
+
class ImageUploader < Shrine
|
749
|
+
def generate_location(io, record: nil, name: nil, derivative: nil, metadata: {}, **options)
|
750
|
+
storage_key #=> :cache, :store, ...
|
751
|
+
io #=> #<File>, #<Shrine::UploadedFile>, ...
|
752
|
+
record #=> #<Photo>, #<User>, ...
|
753
|
+
name #=> :image, :avatar, ...
|
754
|
+
derivative #=> :small, :medium, :large, ... (derivatives plugin)
|
755
|
+
metadata #=> { "filename" => "nature.jpg", "mime_type" => "image/jpeg", "size" => 18573, ... }
|
756
|
+
options #=> { ... other uploader options ... }
|
757
|
+
|
758
|
+
# ...
|
759
|
+
end
|
760
|
+
end
|
761
|
+
```
|
690
762
|
|
691
763
|
## Direct uploads
|
692
764
|
|
@@ -721,7 +793,7 @@ Shrine.plugin :upload_endpoint
|
|
721
793
|
# config/routes.rb (Rails)
|
722
794
|
Rails.application.routes.draw do
|
723
795
|
# ...
|
724
|
-
mount
|
796
|
+
mount Shrine.upload_endpoint(:cache) => "/upload" # POST /upload
|
725
797
|
end
|
726
798
|
```
|
727
799
|
|
@@ -848,7 +920,7 @@ end
|
|
848
920
|
class PromoteJob
|
849
921
|
include Sidekiq::Worker
|
850
922
|
|
851
|
-
def perform(attacher_class, record_class,
|
923
|
+
def perform(attacher_class, record_class, record_id, name, file_data)
|
852
924
|
attacher_class = Object.const_get(attacher_class)
|
853
925
|
record = Object.const_get(record_class).find(record_id) # if using Active Record
|
854
926
|
|
@@ -892,6 +964,8 @@ s3 = Shrine.storages[:cache]
|
|
892
964
|
s3.clear! { |object| object.last_modified < Time.now - 7*24*60*60 } # delete files older than 1 week
|
893
965
|
```
|
894
966
|
|
967
|
+
For S3, it may be easier and cheaper to use [S3 bucket lifecycle expiration rules](http://docs.aws.amazon.com/AmazonS3/latest/UG/lifecycle-configuration-bucket-no-versioning.html) instead.
|
968
|
+
|
895
969
|
## Logging
|
896
970
|
|
897
971
|
The [`instrumentation`][instrumentation plugin] plugin sends and logs events for
|
@@ -905,7 +979,7 @@ uploaded_file.exists?
|
|
905
979
|
uploaded_file.download
|
906
980
|
uploaded_file.delete
|
907
981
|
```
|
908
|
-
```
|
982
|
+
```
|
909
983
|
Metadata (32ms) – {:storage=>:store, :io=>StringIO, :uploader=>Shrine}
|
910
984
|
Upload (1523ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :io=>StringIO, :upload_options=>{}, :uploader=>Shrine}
|
911
985
|
Exists (755ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :uploader=>Shrine}
|
@@ -942,7 +1016,6 @@ In tests you might want to tell Shrine to log only warnings:
|
|
942
1016
|
Shrine.logger.level = Logger::WARN
|
943
1017
|
```
|
944
1018
|
|
945
|
-
[Advantages of Shrine]: https://shrinerb.com/docs/advantages
|
946
1019
|
[Creating Plugins]: https://shrinerb.com/docs/creating-plugins
|
947
1020
|
[Creating Storages]: https://shrinerb.com/docs/creating-storages
|
948
1021
|
[Direct Uploads to S3]: https://shrinerb.com/docs/direct-s3
|
@@ -953,24 +1026,19 @@ Shrine.logger.level = Logger::WARN
|
|
953
1026
|
[Using Attacher]: https://shrinerb.com/docs/attacher
|
954
1027
|
[FileSystem]: https://shrinerb.com/docs/storage/file-system
|
955
1028
|
[S3]: https://shrinerb.com/docs/storage/s3
|
1029
|
+
[Memory]: https://shrinerb.com/docs/storage/memory
|
1030
|
+
[Testing with Shrine]: https://shrinerb.com/docs/testing
|
956
1031
|
[`Shrine::UploadedFile`]: https://shrinerb.com/rdoc/classes/Shrine/UploadedFile/InstanceMethods.html
|
957
1032
|
|
958
1033
|
[attacher]: #attacher
|
959
|
-
[attachment]: #
|
960
|
-
[backgrounding]: #backgrounding
|
1034
|
+
[attachment]: #attaching
|
961
1035
|
[direct uploads]: #direct-uploads
|
962
1036
|
[io abstraction]: #io-abstraction
|
963
1037
|
[location]: #location
|
964
1038
|
[metadata]: #metadata
|
965
|
-
[up front]: #processing-up-front
|
966
|
-
[on-the-fly]: #processing-on-the-fly
|
967
|
-
[plugin system]: #plugin-system
|
968
|
-
[simple upload]: #simple-direct-upload
|
969
1039
|
[presigned upload]: #presigned-direct-upload
|
970
|
-
[resumable upload]: #resumable-direct-upload
|
971
1040
|
[storage]: #storage
|
972
1041
|
[uploaded file]: #uploaded-file
|
973
|
-
[uploading]: #uploading
|
974
1042
|
[uploader]: #uploader
|
975
1043
|
[validation]: #validation
|
976
1044
|
|
@@ -1000,10 +1068,12 @@ Shrine.logger.level = Logger::WARN
|
|
1000
1068
|
[ImageProcessing::MiniMagick]: https://github.com/janko/image_processing/blob/master/doc/minimagick.md#readme
|
1001
1069
|
[ImageProcessing::Vips]: https://github.com/janko/image_processing/blob/master/doc/vips.md#readme
|
1002
1070
|
[`file`]: http://linux.die.net/man/1/file
|
1071
|
+
[Down]: https://github.com/janko/down
|
1003
1072
|
|
1004
1073
|
[activerecord plugin]: https://shrinerb.com/docs/plugins/activerecord
|
1005
1074
|
[add_metadata plugin]: https://shrinerb.com/docs/plugins/add_metadata
|
1006
1075
|
[backgrounding plugin]: https://shrinerb.com/docs/plugins/backgrounding
|
1076
|
+
[data_uri plugin]: https://shrinerb.com/docs/plugins/data_uri
|
1007
1077
|
[derivation_endpoint plugin]: https://shrinerb.com/docs/plugins/derivation_endpoint
|
1008
1078
|
[derivatives plugin]: https://shrinerb.com/docs/plugins/derivatives
|
1009
1079
|
[determine_mime_type plugin]: https://shrinerb.com/docs/plugins/determine_mime_type
|