shrine 3.0.1 → 3.3.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/CHANGELOG.md +82 -0
- data/LICENSE.txt +1 -1
- data/README.md +15 -5
- data/doc/advantages.md +33 -16
- data/doc/attacher.md +2 -2
- data/doc/carrierwave.md +78 -34
- data/doc/changing_derivatives.md +39 -39
- data/doc/design.md +134 -85
- data/doc/direct_s3.md +1 -0
- data/doc/external/articles.md +57 -45
- data/doc/external/extensions.md +41 -35
- data/doc/external/misc.md +23 -8
- data/doc/getting_started.md +177 -112
- data/doc/metadata.md +79 -43
- data/doc/multiple_files.md +6 -4
- data/doc/paperclip.md +119 -42
- data/doc/plugins/activerecord.md +1 -1
- data/doc/plugins/add_metadata.md +112 -35
- data/doc/plugins/atomic_helpers.md +41 -3
- 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 +238 -171
- data/doc/plugins/determine_mime_type.md +2 -2
- data/doc/plugins/download_endpoint.md +5 -5
- data/doc/plugins/dynamic_storage.md +1 -1
- data/doc/plugins/form_assign.md +5 -5
- data/doc/plugins/included.md +25 -5
- data/doc/plugins/infer_extension.md +11 -2
- data/doc/plugins/instrumentation.md +1 -1
- data/doc/plugins/metadata_attributes.md +22 -10
- data/doc/plugins/mirroring.md +1 -1
- data/doc/plugins/persistence.md +11 -1
- 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/signature.md +11 -2
- data/doc/plugins/store_dimensions.md +12 -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 +4 -4
- data/doc/plugins/validation.md +14 -4
- data/doc/plugins/validation_helpers.md +3 -3
- data/doc/plugins/versions.md +7 -7
- data/doc/processing.md +290 -127
- data/doc/refile.md +39 -18
- data/doc/release_notes/2.19.0.md +1 -1
- data/doc/release_notes/2.8.0.md +1 -1
- data/doc/release_notes/3.0.0.md +1 -1
- data/doc/release_notes/3.0.1.md +4 -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 +31 -0
- data/doc/release_notes/3.2.2.md +14 -0
- data/doc/release_notes/3.3.0.md +105 -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 -82
- data/doc/testing.md +2 -2
- data/doc/upgrading_to_3.md +97 -49
- data/doc/validation.md +3 -2
- data/lib/shrine.rb +8 -8
- data/lib/shrine/attacher.rb +24 -14
- data/lib/shrine/attachment.rb +5 -5
- data/lib/shrine/plugins.rb +22 -0
- data/lib/shrine/plugins/activerecord.rb +1 -1
- data/lib/shrine/plugins/add_metadata.rb +18 -7
- data/lib/shrine/plugins/backgrounding.rb +2 -2
- 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 +12 -7
- data/lib/shrine/plugins/derivatives.rb +61 -29
- data/lib/shrine/plugins/determine_mime_type.rb +3 -3
- data/lib/shrine/plugins/entity.rb +6 -6
- data/lib/shrine/plugins/mirroring.rb +8 -8
- data/lib/shrine/plugins/model.rb +3 -3
- data/lib/shrine/plugins/presign_endpoint.rb +16 -4
- 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_attachment.rb +5 -0
- data/lib/shrine/plugins/remove_invalid.rb +10 -5
- data/lib/shrine/plugins/sequel.rb +1 -1
- data/lib/shrine/plugins/signature.rb +7 -6
- data/lib/shrine/plugins/store_dimensions.rb +22 -11
- data/lib/shrine/plugins/type_predicates.rb +113 -0
- data/lib/shrine/plugins/upload_endpoint.rb +10 -5
- 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/memory.rb +5 -3
- data/lib/shrine/storage/s3.rb +117 -38
- data/lib/shrine/uploaded_file.rb +0 -1
- data/lib/shrine/version.rb +2 -2
- data/shrine.gemspec +7 -8
- metadata +25 -31
data/doc/external/extensions.md
CHANGED
@@ -4,44 +4,50 @@ 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
|
-
|
40
|
-
|
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-blurhash](https://github.com/renchap/shrine-blurhash) | Plugin for computing [Blurhash](https://blurha.sh/) on images |
|
32
|
+
| [shrine-cloudimage](https://github.com/janklimo/shrine-cloudimage) | Plugin for [Cloudimage](https://www.cloudimage.io/) |
|
33
|
+
| [shrine-color](https://github.com/jnylen/shrine-color) | Plugin for finding dominant color in an image |
|
34
|
+
| [shrine-configurable_storage](https://github.com/SleeplessByte/shrine-configurable_storage) | Plugin for lazy storage registration |
|
35
|
+
| [shrine-content_addressable](https://github.com/SleeplessByte/shrine-content_addressable) | Plugin for generating content addressable locations |
|
36
|
+
| [shrine-imgix](https://github.com/shrinerb/shrine-imgix) | Plugin for [Imgix](https://www.imgix.com/) |
|
37
|
+
| [shrine-transloadit](https://github.com/shrinerb/shrine-transloadit) | Plugin for [Transloadit](https://transloadit.com/) |
|
38
|
+
| [shrine-lambda](https://github.com/texpert/shrine-lambda) | Plugin for [AWS Lambda](https://aws.amazon.com/lambda/) |
|
39
|
+
| [hanami-shrine](https://github.com/katafrakt/hanami-shrine) | Plugin for [Hanami](https://hanamirb.org/) |
|
40
|
+
| [shrine-mongoid](https://github.com/shrinerb/shrine-mongoid) | Plugin for [Mongoid](https://mongoid.org) |
|
41
|
+
| [shrine-rails](https://github.com/abepetrillo/shrine-rails) | Plugin for [Rails](https://rubyonrails.org/) |
|
42
|
+
| [shrine-rom](https://github.com/shrinerb/shrine-rom) | Plugin for [ROM](https://rom-rb.org/) |
|
43
|
+
| [shrine-tus](https://github.com/shrinerb/shrine-tus) | Plugin for [tus](https://tus.io) server integration |
|
41
44
|
|
42
45
|
## Libraries
|
43
46
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
47
|
+
| Gem | Description |
|
48
|
+
| :----- | :------- |
|
49
|
+
| [ckeditor](https://github.com/galetahub/ckeditor) | Integration for [CKEditor](https://ckeditor.com/ckeditor-4/) |
|
50
|
+
| [faster_s3_url](https://github.com/jrochkind/faster_s3_url) | Optimized generation of public and presigned AWS S3 GET URLs |
|
51
|
+
| [imgproxy](https://github.com/imgproxy/imgproxy.rb) | Integration for [imgproxy](https://github.com/imgproxy/imgproxy) |
|
52
|
+
| [rails_admin](https://github.com/sferik/rails_admin) | Integration for [RailsAdmin](https://github.com/sferik/rails_admin) |
|
53
|
+
| [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/external/misc.md
CHANGED
@@ -4,14 +4,29 @@ title: Miscellaneous
|
|
4
4
|
|
5
5
|
## Demos
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
| Demo | Description |
|
8
|
+
| :--- | :---------- |
|
9
|
+
| [Dropzone demo](https://github.com/codyeatworld/example-shrine-dropzone) | Shows direct upload using [Dropzone.js] |
|
10
|
+
| [Hanami demo](https://github.com/katafrakt/hanami-shrine-example) | Shows file attachment in [Hanami] |
|
11
|
+
| [Crop demo](https://github.com/shrinerb/shrine-crop-example) | Shows image cropping using [Cropper.js] |
|
12
|
+
| [Rails demo](https://github.com/erikdahlstrand/shrine-rails-example) | Shows direct upload in [Rails] |
|
13
|
+
| [Resumable uploads demo](https://github.com/shrinerb/shrine-tus-demo) | Shows resumable direct upload on [tus] |
|
14
|
+
| [Roda demo (official)](https://github.com/shrinerb/shrine/tree/master/demo) | Shows direct upload in [Roda] |
|
15
|
+
| [rom-rb & dry-rb demo](https://github.com/shrinerb/shrine-rom/tree/master/demo) | Shows file attachment with [rom-rb] and [dry-rb] |
|
16
|
+
| [Transloadit demo](https://github.com/shrinerb/shrine-transloadit/tree/master/demo) | Shows file processing using [Transloadit] |
|
14
17
|
|
15
18
|
## Projects
|
16
19
|
|
17
|
-
|
20
|
+
| Project | Description |
|
21
|
+
| :------ | :---------- |
|
22
|
+
| [CortexCMS](https://docs.cortexcms.org) | An open source, enterprise content management and distribution platform |
|
23
|
+
|
24
|
+
[Dropzone.js]: https://www.dropzonejs.com/
|
25
|
+
[Hanami]: https://hanamirb.org/
|
26
|
+
[Cropper.js]: https://github.com/fengyuanchen/cropperjs
|
27
|
+
[Rails]: https://rubyonrails.org/
|
28
|
+
[tus]: https://tus.io/
|
29
|
+
[Roda]: https://roda.jeremyevans.net/
|
30
|
+
[rom-rb]: https://rom-rb.org/
|
31
|
+
[dry-rb]: https://dry-rb.org/
|
32
|
+
[Transloadit]: https://transloadit.com/
|
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,13 +309,12 @@ record [attachment]. It contains the following data:
|
|
287
309
|
| `metadata` | file [metadata] that was extracted before upload |
|
288
310
|
|
289
311
|
```rb
|
290
|
-
uploaded_file = uploader.upload(file)
|
291
312
|
uploaded_file #=> #<Shrine::UploadedFile id="949sdjg834.jpg" storage=:store metadata={...}>
|
292
313
|
|
293
|
-
uploaded_file.id
|
294
|
-
uploaded_file.storage_key
|
295
|
-
uploaded_file.storage
|
296
|
-
uploaded_file.metadata
|
314
|
+
uploaded_file.id #=> "949sdjg834.jpg"
|
315
|
+
uploaded_file.storage_key #=> :store
|
316
|
+
uploaded_file.storage #=> #<Shrine::Storage::S3>
|
317
|
+
uploaded_file.metadata #=> {...}
|
297
318
|
```
|
298
319
|
|
299
320
|
It comes with many convenient methods that delegate to the storage:
|
@@ -341,7 +362,7 @@ The easiest way to attach files is with the `Shrine::Attachment` module:
|
|
341
362
|
```rb
|
342
363
|
class Photo < Sequel::Model # ActiveRecord::Base
|
343
364
|
include ImageUploader::Attachment.new(:image) #
|
344
|
-
include ImageUploader::Attachment[:image] # use
|
365
|
+
include ImageUploader::Attachment[:image] # use your preferred syntax
|
345
366
|
include ImageUploader::Attachment(:image) #
|
346
367
|
end
|
347
368
|
```
|
@@ -364,7 +385,7 @@ that attachments are deleted when the record is destroyed.
|
|
364
385
|
photo.image #=> nil
|
365
386
|
|
366
387
|
# the assigned file is cached to temporary storage and written to `image_data` column
|
367
|
-
photo.image = File.open("waterfall.jpg")
|
388
|
+
photo.image = File.open("waterfall.jpg", "rb")
|
368
389
|
photo.image #=> #<Shrine::UploadedFile ...>
|
369
390
|
photo.image_url #=> "/uploads/cache/0sdfllasfi842.jpg"
|
370
391
|
photo.image_data #=> '{"id":"0sdfllasfi842.jpg","storage":"cache","metadata":{...}}'
|
@@ -408,38 +429,36 @@ attacher.url # equivalent to `photo.image_url`
|
|
408
429
|
```
|
409
430
|
|
410
431
|
The attacher is what drives attaching files to model instances; you can use it
|
411
|
-
as a more explicit alternative to models' attachment interface, or
|
412
|
-
|
432
|
+
as a more explicit alternative to models' attachment interface, or when you
|
433
|
+
need something that's not available through the attachment methods.
|
413
434
|
|
414
|
-
|
415
|
-
attacher uses, or upload files directly to permanent storage. See the [Using
|
416
|
-
Attacher] guide for more details.
|
435
|
+
See [Using Attacher] guide for more details.
|
417
436
|
|
418
437
|
### Temporary storage
|
419
438
|
|
420
|
-
Shrine uses temporary storage to support
|
421
|
-
|
422
|
-
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:
|
423
442
|
|
424
443
|
```rb
|
425
444
|
Shrine.plugin :model, cache: false
|
426
445
|
```
|
427
|
-
<!--DOCUSAURUS_CODE_TABS-->
|
428
|
-
<!--Attachment-->
|
429
446
|
```rb
|
430
|
-
photo.image = File.open("waterfall.jpg")
|
447
|
+
photo.image = File.open("waterfall.jpg", "rb")
|
431
448
|
photo.image.storage_key #=> :store
|
432
449
|
```
|
433
|
-
|
450
|
+
|
451
|
+
If you're using the attacher directly, you can just use `Attacher#attach`
|
452
|
+
instead of `Attacher#assign`:
|
453
|
+
|
434
454
|
```rb
|
435
|
-
attacher.attach File.open("waterfall.jpg")
|
455
|
+
attacher.attach File.open("waterfall.jpg", "rb")
|
436
456
|
attacher.file.storage_key #=> :store
|
437
457
|
```
|
438
|
-
<!--END_DOCUSAURUS_CODE_TABS-->
|
439
458
|
|
440
459
|
## Plugin system
|
441
460
|
|
442
|
-
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
|
443
462
|
functionality. All additional features are available via [plugins], which also
|
444
463
|
ship with Shrine. This way you can choose exactly what and how much Shrine does
|
445
464
|
for you, and you load the code only for features that you use.
|
@@ -497,7 +516,7 @@ gem "marcel", "~> 0.3"
|
|
497
516
|
Shrine.plugin :determine_mime_type, analyzer: :marcel
|
498
517
|
```
|
499
518
|
```rb
|
500
|
-
photo = Photo.
|
519
|
+
photo = Photo.new(image: StringIO.new("<?php ... ?>"))
|
501
520
|
photo.image.mime_type #=> "application/x-php"
|
502
521
|
```
|
503
522
|
|
@@ -510,36 +529,38 @@ the [Extracting Metadata] guide for more details.
|
|
510
529
|
|
511
530
|
## Processing
|
512
531
|
|
513
|
-
Shrine allows you to process attached files
|
514
|
-
example, if your app is accepting image uploads, you can generate a
|
515
|
-
set of of thumbnails when the image is attached to a record, or you
|
516
|
-
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.
|
517
536
|
|
518
537
|
For image processing, it's recommended to use the **[ImageProcessing]** gem,
|
519
538
|
which is a high-level wrapper for processing with
|
520
539
|
[MiniMagick][ImageProcessing::MiniMagick] and [libvips][ImageProcessing::Vips].
|
521
540
|
|
522
|
-
```
|
541
|
+
```
|
523
542
|
$ brew install imagemagick vips
|
524
543
|
```
|
525
544
|
|
526
|
-
###
|
545
|
+
### Eager processing
|
527
546
|
|
528
|
-
|
529
|
-
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:
|
530
551
|
|
531
552
|
```rb
|
532
553
|
# Gemfile
|
533
554
|
gem "image_processing", "~> 1.8"
|
534
555
|
```
|
535
556
|
```rb
|
536
|
-
Shrine.plugin :derivatives
|
557
|
+
Shrine.plugin :derivatives, create_on_promote: true
|
537
558
|
```
|
538
559
|
```rb
|
539
560
|
require "image_processing/mini_magick"
|
540
561
|
|
541
562
|
class ImageUploader < Shrine
|
542
|
-
Attacher.
|
563
|
+
Attacher.derivatives do |original|
|
543
564
|
magick = ImageProcessing::MiniMagick.source(original)
|
544
565
|
|
545
566
|
{
|
@@ -552,40 +573,34 @@ end
|
|
552
573
|
```
|
553
574
|
```rb
|
554
575
|
photo = Photo.new(image: file)
|
555
|
-
photo.
|
556
|
-
photo.save
|
576
|
+
photo.save # automatically creates derivatives on promotion
|
557
577
|
```
|
558
578
|
|
559
|
-
|
560
|
-
route make sure to trigger derivatives creation for new attachments:
|
579
|
+
You can then retrieve the URL of a processed derivative:
|
561
580
|
|
562
581
|
```rb
|
563
|
-
photo.
|
582
|
+
photo.image_url(:large) #=> "https://s3.amazonaws.com/path/to/large.jpg"
|
564
583
|
```
|
565
584
|
|
566
|
-
|
567
|
-
|
568
|
-
[`Shrine::UploadedFile`][uploaded file] objects:
|
585
|
+
The derivatives data is stored in the `<attachment>_data` column, and you can
|
586
|
+
retrieve them as [`Shrine::UploadedFile`][uploaded file] objects:
|
569
587
|
|
570
588
|
```rb
|
571
|
-
photo.image(:large)
|
572
|
-
photo.image(:large).url
|
573
|
-
photo.image(:large).size
|
574
|
-
photo.image(:large).mime_type
|
589
|
+
photo.image(:large) #=> #<Shrine::UploadedFile id="path/to/large.jpg" storage=:store metadata={...}>
|
590
|
+
photo.image(:large).url #=> "https://s3.amazonaws.com/path/to/large.jpg"
|
591
|
+
photo.image(:large).size #=> 5825949
|
592
|
+
photo.image(:large).mime_type #=> "image/jpeg"
|
575
593
|
```
|
576
594
|
|
577
|
-
For more details, see the [
|
578
|
-
|
595
|
+
For more details, see the [File Processing] guide and the
|
596
|
+
[`derivatives`][derivatives plugin] plugin documentation.
|
579
597
|
|
580
|
-
###
|
598
|
+
### On-the-fly processing
|
581
599
|
|
582
600
|
On-the-fly processing is provided by the
|
583
|
-
[`derivation_endpoint`][derivation_endpoint plugin] plugin.
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
To set it up, we mount the Rack app in our router on a chosen path prefix,
|
588
|
-
configure the plugin with a secret key and that path prefix, and define
|
601
|
+
[`derivation_endpoint`][derivation_endpoint plugin] plugin. To set it up, we
|
602
|
+
configure the plugin with a secret key and a path prefix, [mount][Mounting
|
603
|
+
Endpoints] its Rack app in our routes on the configured path prefix, and define
|
589
604
|
processing we want to perform:
|
590
605
|
|
591
606
|
```rb
|
@@ -593,19 +608,15 @@ processing we want to perform:
|
|
593
608
|
gem "image_processing", "~> 1.8"
|
594
609
|
```
|
595
610
|
```rb
|
596
|
-
# config/
|
597
|
-
|
598
|
-
|
599
|
-
mount ImageUploader.derivation_endpoint => "/derivations/image"
|
600
|
-
end
|
611
|
+
# config/initializers/rails.rb (Rails)
|
612
|
+
# ...
|
613
|
+
Shrine.plugin :derivation_endpoint, secret_key: "<YOUR_SECRET_KEY>"
|
601
614
|
```
|
602
615
|
```rb
|
603
616
|
require "image_processing/mini_magick"
|
604
617
|
|
605
618
|
class ImageUploader < Shrine
|
606
|
-
plugin :derivation_endpoint,
|
607
|
-
secret_key: "<YOUR SECRET KEY>",
|
608
|
-
prefix: "derivations/image" # needs to match the mount point in routes
|
619
|
+
plugin :derivation_endpoint, prefix: "derivations/image" # matches mount point
|
609
620
|
|
610
621
|
derivation :thumbnail do |file, width, height|
|
611
622
|
ImageProcessing::MiniMagick
|
@@ -614,6 +625,13 @@ class ImageUploader < Shrine
|
|
614
625
|
end
|
615
626
|
end
|
616
627
|
```
|
628
|
+
```rb
|
629
|
+
# config/routes.rb (Rails)
|
630
|
+
Rails.application.routes.draw do
|
631
|
+
# ...
|
632
|
+
mount ImageUploader.derivation_endpoint => "/derivations/image"
|
633
|
+
end
|
634
|
+
```
|
617
635
|
|
618
636
|
Now we can generate URLs from attached files that will perform the desired
|
619
637
|
processing:
|
@@ -658,36 +676,85 @@ For more details, see the [File Validation] guide and
|
|
658
676
|
|
659
677
|
## Location
|
660
678
|
|
661
|
-
Shrine automatically
|
662
|
-
default the hierarchy is flat, meaning all files are stored in the root
|
663
|
-
directory of the storage.
|
664
|
-
|
665
|
-
|
679
|
+
Shrine automatically generates random locations before uploading files. By
|
680
|
+
default, the hierarchy is flat, meaning all files are stored in the root
|
681
|
+
directory of the storage.
|
682
|
+
|
683
|
+
```
|
684
|
+
024d9fe83bf4fafb.jpg
|
685
|
+
768a336bf54de219.jpg
|
686
|
+
adfaa363629f7fc5.png
|
687
|
+
...
|
688
|
+
```
|
689
|
+
|
690
|
+
The [`pretty_location`][pretty_location plugin] plugin provides a good default
|
691
|
+
hierarchy:
|
692
|
+
|
693
|
+
```rb
|
694
|
+
Shrine.plugin :pretty_location
|
695
|
+
```
|
696
|
+
```
|
697
|
+
user/
|
698
|
+
564/
|
699
|
+
avatar/
|
700
|
+
aa3e0cd715.jpg
|
701
|
+
thumb-493g82jf23.jpg
|
702
|
+
photo/
|
703
|
+
123/
|
704
|
+
image/
|
705
|
+
13f8a7bc18.png
|
706
|
+
thumb-9be62da67e.png
|
707
|
+
...
|
708
|
+
```
|
709
|
+
|
710
|
+
But you can also override `Shrine#generate_location` with a custom
|
711
|
+
implementation, for example:
|
666
712
|
|
667
713
|
```rb
|
668
714
|
class ImageUploader < Shrine
|
669
715
|
def generate_location(io, record: nil, derivative: nil, **)
|
670
|
-
|
671
|
-
style = derivative ? "thumbs" : "originals"
|
672
|
-
name = super # the default unique identifier
|
716
|
+
return super unless record
|
673
717
|
|
674
|
-
|
718
|
+
table = record.class.table_name
|
719
|
+
id = record.id
|
720
|
+
prefix = derivative || "original"
|
721
|
+
|
722
|
+
"uploads/#{table}/#{id}/#{prefix}-#{super}"
|
675
723
|
end
|
676
724
|
end
|
677
725
|
```
|
678
|
-
```
|
726
|
+
```
|
679
727
|
uploads/
|
680
728
|
photos/
|
681
|
-
|
682
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
729
|
+
123/
|
730
|
+
original-afe929b8b4.jpg
|
731
|
+
small-ad61f25883.jpg
|
732
|
+
medium-41b75c42bb.jpg
|
733
|
+
large-73e67abe50.jpg
|
734
|
+
...
|
686
735
|
```
|
687
736
|
|
688
|
-
|
689
|
-
|
690
|
-
|
737
|
+
> There should always be a random component in the location, so that the ORM
|
738
|
+
dirty tracking is detected properly.
|
739
|
+
|
740
|
+
The `Shrine#generate_location` method contains a lot of useful context for the
|
741
|
+
upcoming upload:
|
742
|
+
|
743
|
+
```rb
|
744
|
+
class ImageUploader < Shrine
|
745
|
+
def generate_location(io, record: nil, name: nil, derivative: nil, metadata: {}, **options)
|
746
|
+
storage_key #=> :cache, :store, ...
|
747
|
+
io #=> #<File>, #<Shrine::UploadedFile>, ...
|
748
|
+
record #=> #<Photo>, #<User>, ...
|
749
|
+
name #=> :image, :avatar, ...
|
750
|
+
derivative #=> :small, :medium, :large, ... (derivatives plugin)
|
751
|
+
metadata #=> { "filename" => "nature.jpg", "mime_type" => "image/jpeg", "size" => 18573, ... }
|
752
|
+
options #=> { ... other uploader options ... }
|
753
|
+
|
754
|
+
# ...
|
755
|
+
end
|
756
|
+
end
|
757
|
+
```
|
691
758
|
|
692
759
|
## Direct uploads
|
693
760
|
|
@@ -722,7 +789,7 @@ Shrine.plugin :upload_endpoint
|
|
722
789
|
# config/routes.rb (Rails)
|
723
790
|
Rails.application.routes.draw do
|
724
791
|
# ...
|
725
|
-
mount
|
792
|
+
mount Shrine.upload_endpoint(:cache) => "/upload" # POST /upload
|
726
793
|
end
|
727
794
|
```
|
728
795
|
|
@@ -832,7 +899,7 @@ resumable uploads from scratch, it includes a complete JavaScript example
|
|
832
899
|
|
833
900
|
## Backgrounding
|
834
901
|
|
835
|
-
The [`backgrounding`][backgrounding plugin] allows you to move file promotion
|
902
|
+
The [`backgrounding`][backgrounding plugin] plugin allows you to move file promotion
|
836
903
|
and deletion into a background job, using the backgrounding library [of your
|
837
904
|
choice][Backgrounding Libraries]:
|
838
905
|
|
@@ -849,7 +916,7 @@ end
|
|
849
916
|
class PromoteJob
|
850
917
|
include Sidekiq::Worker
|
851
918
|
|
852
|
-
def perform(attacher_class, record_class,
|
919
|
+
def perform(attacher_class, record_class, record_id, name, file_data)
|
853
920
|
attacher_class = Object.const_get(attacher_class)
|
854
921
|
record = Object.const_get(record_class).find(record_id) # if using Active Record
|
855
922
|
|
@@ -893,6 +960,8 @@ s3 = Shrine.storages[:cache]
|
|
893
960
|
s3.clear! { |object| object.last_modified < Time.now - 7*24*60*60 } # delete files older than 1 week
|
894
961
|
```
|
895
962
|
|
963
|
+
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.
|
964
|
+
|
896
965
|
## Logging
|
897
966
|
|
898
967
|
The [`instrumentation`][instrumentation plugin] plugin sends and logs events for
|
@@ -906,7 +975,7 @@ uploaded_file.exists?
|
|
906
975
|
uploaded_file.download
|
907
976
|
uploaded_file.delete
|
908
977
|
```
|
909
|
-
```
|
978
|
+
```
|
910
979
|
Metadata (32ms) – {:storage=>:store, :io=>StringIO, :uploader=>Shrine}
|
911
980
|
Upload (1523ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :io=>StringIO, :upload_options=>{}, :uploader=>Shrine}
|
912
981
|
Exists (755ms) – {:storage=>:store, :location=>"ed0e30ddec8b97813f2c1f4cfd1700b4", :uploader=>Shrine}
|
@@ -943,7 +1012,6 @@ In tests you might want to tell Shrine to log only warnings:
|
|
943
1012
|
Shrine.logger.level = Logger::WARN
|
944
1013
|
```
|
945
1014
|
|
946
|
-
[Advantages of Shrine]: https://shrinerb.com/docs/advantages
|
947
1015
|
[Creating Plugins]: https://shrinerb.com/docs/creating-plugins
|
948
1016
|
[Creating Storages]: https://shrinerb.com/docs/creating-storages
|
949
1017
|
[Direct Uploads to S3]: https://shrinerb.com/docs/direct-s3
|
@@ -954,24 +1022,19 @@ Shrine.logger.level = Logger::WARN
|
|
954
1022
|
[Using Attacher]: https://shrinerb.com/docs/attacher
|
955
1023
|
[FileSystem]: https://shrinerb.com/docs/storage/file-system
|
956
1024
|
[S3]: https://shrinerb.com/docs/storage/s3
|
1025
|
+
[Memory]: https://shrinerb.com/docs/storage/memory
|
1026
|
+
[Testing with Shrine]: https://shrinerb.com/docs/testing
|
957
1027
|
[`Shrine::UploadedFile`]: https://shrinerb.com/rdoc/classes/Shrine/UploadedFile/InstanceMethods.html
|
958
1028
|
|
959
1029
|
[attacher]: #attacher
|
960
|
-
[attachment]: #
|
961
|
-
[backgrounding]: #backgrounding
|
1030
|
+
[attachment]: #attaching
|
962
1031
|
[direct uploads]: #direct-uploads
|
963
1032
|
[io abstraction]: #io-abstraction
|
964
1033
|
[location]: #location
|
965
1034
|
[metadata]: #metadata
|
966
|
-
[up front]: #processing-up-front
|
967
|
-
[on-the-fly]: #processing-on-the-fly
|
968
|
-
[plugin system]: #plugin-system
|
969
|
-
[simple upload]: #simple-direct-upload
|
970
1035
|
[presigned upload]: #presigned-direct-upload
|
971
|
-
[resumable upload]: #resumable-direct-upload
|
972
1036
|
[storage]: #storage
|
973
1037
|
[uploaded file]: #uploaded-file
|
974
|
-
[uploading]: #uploading
|
975
1038
|
[uploader]: #uploader
|
976
1039
|
[validation]: #validation
|
977
1040
|
|
@@ -1001,10 +1064,12 @@ Shrine.logger.level = Logger::WARN
|
|
1001
1064
|
[ImageProcessing::MiniMagick]: https://github.com/janko/image_processing/blob/master/doc/minimagick.md#readme
|
1002
1065
|
[ImageProcessing::Vips]: https://github.com/janko/image_processing/blob/master/doc/vips.md#readme
|
1003
1066
|
[`file`]: http://linux.die.net/man/1/file
|
1067
|
+
[Down]: https://github.com/janko/down
|
1004
1068
|
|
1005
1069
|
[activerecord plugin]: https://shrinerb.com/docs/plugins/activerecord
|
1006
1070
|
[add_metadata plugin]: https://shrinerb.com/docs/plugins/add_metadata
|
1007
1071
|
[backgrounding plugin]: https://shrinerb.com/docs/plugins/backgrounding
|
1072
|
+
[data_uri plugin]: https://shrinerb.com/docs/plugins/data_uri
|
1008
1073
|
[derivation_endpoint plugin]: https://shrinerb.com/docs/plugins/derivation_endpoint
|
1009
1074
|
[derivatives plugin]: https://shrinerb.com/docs/plugins/derivatives
|
1010
1075
|
[determine_mime_type plugin]: https://shrinerb.com/docs/plugins/determine_mime_type
|