activestorage 7.1.5.2 → 8.1.2.1
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 +96 -295
- data/README.md +8 -5
- data/app/assets/javascripts/activestorage.esm.js +38 -2
- data/app/assets/javascripts/activestorage.js +38 -1
- data/app/controllers/active_storage/direct_uploads_controller.rb +1 -1
- data/app/controllers/active_storage/disk_controller.rb +6 -2
- data/app/controllers/concerns/active_storage/streaming.rb +17 -1
- data/app/javascript/activestorage/direct_upload_controller.js +48 -1
- data/app/javascript/activestorage/index.js +2 -1
- data/app/models/active_storage/attachment.rb +4 -4
- data/app/models/active_storage/blob/representable.rb +71 -5
- data/app/models/active_storage/blob.rb +43 -16
- data/app/models/active_storage/filename.rb +2 -5
- data/app/models/active_storage/preview.rb +14 -6
- data/app/models/active_storage/variant.rb +12 -12
- data/app/models/active_storage/variation.rb +1 -1
- data/db/migrate/20170806125915_create_active_storage_tables.rb +1 -1
- data/lib/active_storage/analyzer/image_analyzer/image_magick.rb +11 -4
- data/lib/active_storage/analyzer/image_analyzer/vips.rb +28 -14
- data/lib/active_storage/analyzer/image_analyzer.rb +5 -0
- data/lib/active_storage/analyzer/video_analyzer.rb +6 -2
- data/lib/active_storage/attached/changes/create_one.rb +6 -1
- data/lib/active_storage/attached/changes/create_one_of_many.rb +5 -1
- data/lib/active_storage/attached/model.rb +56 -30
- data/lib/active_storage/attached.rb +0 -1
- data/lib/active_storage/engine.rb +40 -15
- data/lib/active_storage/errors.rb +4 -0
- data/lib/active_storage/fixture_set.rb +1 -1
- data/lib/active_storage/gem_version.rb +3 -3
- data/lib/active_storage/log_subscriber.rb +1 -1
- data/lib/active_storage/previewer/mupdf_previewer.rb +6 -2
- data/lib/active_storage/previewer/poppler_pdf_previewer.rb +6 -2
- data/lib/active_storage/previewer/video_previewer.rb +1 -1
- data/lib/active_storage/service/configurator.rb +6 -0
- data/lib/active_storage/service/disk_service.rb +46 -2
- data/lib/active_storage/service/gcs_service.rb +15 -5
- data/lib/active_storage/service/mirror_service.rb +13 -4
- data/lib/active_storage/service/registry.rb +6 -0
- data/lib/active_storage/service/s3_service.rb +19 -4
- data/lib/active_storage/service.rb +5 -1
- data/lib/active_storage/structured_event_subscriber.rb +79 -0
- data/lib/active_storage/transformers/image_magick.rb +72 -0
- data/lib/active_storage/transformers/image_processing_transformer.rb +6 -68
- data/lib/active_storage/transformers/null_transformer.rb +12 -0
- data/lib/active_storage/transformers/vips.rb +11 -0
- data/lib/active_storage.rb +18 -14
- metadata +18 -15
- data/lib/active_storage/service/azure_storage_service.rb +0 -194
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 95a8a27a89ab0f4be63e4f466fcf5a7eedf15ed806d7b061257ab56aafadc1a6
|
|
4
|
+
data.tar.gz: 4f7046ccc16df5fb3bd06080e914fb54c0ad2009d5dbcb1080dcab7bf945f1e3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f187b18f73b712921f1dc2252fe5a645ce83b84dad1dc97d6b24be9f136a634e2b0da3290d2a8f9284ae6134bfc8b62fc8e7a7b9f4974b9f3dee6a6ed0af17aa
|
|
7
|
+
data.tar.gz: 24b2da33d24f7f2d34623482b2a2c818e3d87d7dfa8a165e1ff6091134548e37b8dc9eb3908a69231d3e8e9fa3320f75db1a0dabcb54b048e4e543319ec2f555
|
data/CHANGELOG.md
CHANGED
|
@@ -1,366 +1,167 @@
|
|
|
1
|
-
## Rails
|
|
1
|
+
## Rails 8.1.2.1 (March 23, 2026) ##
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
* Filter user supplied metadata in DirectUploadController
|
|
4
4
|
|
|
5
|
-
[CVE-
|
|
5
|
+
[CVE-2026-33173]
|
|
6
6
|
|
|
7
|
-
*
|
|
7
|
+
*Jean Boussier*
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* No changes.
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
## Rails 7.1.5 (October 30, 2024) ##
|
|
15
|
-
|
|
16
|
-
* No changes.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
## Rails 7.1.4.2 (October 23, 2024) ##
|
|
20
|
-
|
|
21
|
-
* No changes.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
## Rails 7.1.4.1 (October 15, 2024) ##
|
|
25
|
-
|
|
26
|
-
* No changes.
|
|
9
|
+
* Configurable maxmimum streaming chunk size
|
|
27
10
|
|
|
11
|
+
Makes sure that byte ranges for blobs don't exceed 100mb by default.
|
|
12
|
+
Content ranges that are too big can result in denial of service.
|
|
28
13
|
|
|
29
|
-
|
|
14
|
+
[CVE-2026-33174]
|
|
30
15
|
|
|
31
|
-
*
|
|
16
|
+
*Gannon McGibbon*
|
|
32
17
|
|
|
33
|
-
|
|
18
|
+
* Limit range requests to a single range
|
|
34
19
|
|
|
20
|
+
[CVE-2026-33658]
|
|
35
21
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
* No changes.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
## Rails 7.1.3.3 (May 16, 2024) ##
|
|
42
|
-
|
|
43
|
-
* No changes.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
## Rails 7.1.3.2 (February 21, 2024) ##
|
|
47
|
-
|
|
48
|
-
* No changes.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
## Rails 7.1.3.1 (February 21, 2024) ##
|
|
52
|
-
|
|
53
|
-
* No changes.
|
|
22
|
+
*Jean Boussier*
|
|
54
23
|
|
|
55
24
|
|
|
56
|
-
|
|
25
|
+
* Prevent path traversal in `DiskService`.
|
|
57
26
|
|
|
58
|
-
|
|
27
|
+
`DiskService#path_for` now raises an `InvalidKeyError` when passed keys with dot segments (".",
|
|
28
|
+
".."), or if the resolved path is outside the storage root directory.
|
|
59
29
|
|
|
60
|
-
|
|
30
|
+
`#path_for` also now consistently raises `InvalidKeyError` if the key is invalid in any way, for
|
|
31
|
+
example containing null bytes or having an incompatible encoding. Previously, the exception
|
|
32
|
+
raised may have been `ArgumentError` or `Encoding::CompatibilityError`.
|
|
61
33
|
|
|
62
|
-
|
|
63
|
-
`ActiveRecord::Base.table_name_prefix` configuration.
|
|
34
|
+
`DiskController` now explicitly rescues `InvalidKeyError` with appropriate HTTP status codes.
|
|
64
35
|
|
|
65
|
-
|
|
36
|
+
[CVE-2026-33195]
|
|
66
37
|
|
|
67
|
-
*
|
|
68
|
-
preview image variant for previewable files.
|
|
38
|
+
*Mike Dalessio*
|
|
69
39
|
|
|
70
|
-
|
|
40
|
+
* Prevent glob injection in `DiskService#delete_prefixed`.
|
|
71
41
|
|
|
72
|
-
|
|
73
|
-
variants.
|
|
42
|
+
Escape glob metacharacters in the resolved path before passing to `Dir.glob`.
|
|
74
43
|
|
|
75
|
-
|
|
44
|
+
Note that this change breaks any existing code that is relying on `delete_prefixed` to expand
|
|
45
|
+
glob metacharacters. This change presumes that is unintended behavior (as other storage services
|
|
46
|
+
do not respect these metacharacters).
|
|
76
47
|
|
|
77
|
-
|
|
48
|
+
[CVE-2026-33202]
|
|
78
49
|
|
|
79
|
-
*
|
|
50
|
+
*Mike Dalessio*
|
|
80
51
|
|
|
81
|
-
* When using the `preprocessed: true` option, avoid enqueuing transform jobs
|
|
82
|
-
for blobs that are not representable.
|
|
83
52
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
* Process preview image variant when calling `ActiveStorage::Preview#processed`.
|
|
87
|
-
For example, `attached_pdf.preview(:thumb).processed` will now immediately
|
|
88
|
-
generate the full-sized preview image and the `:thumb` variant of it.
|
|
89
|
-
Previously, the `:thumb` variant would not be generated until a further call
|
|
90
|
-
to e.g. `processed.url`.
|
|
91
|
-
|
|
92
|
-
*Chedli Bourguiba* and *Jonathan Hefner*
|
|
93
|
-
|
|
94
|
-
* Prevent `ActiveRecord::StrictLoadingViolationError` when strict loading is
|
|
95
|
-
enabled and the variant of an Active Storage preview has already been
|
|
96
|
-
processed (for example, by calling `ActiveStorage::Preview#url`).
|
|
97
|
-
|
|
98
|
-
*Jonathan Hefner*
|
|
99
|
-
|
|
100
|
-
* Fix `preprocessed: true` option for named variants of previewable files.
|
|
101
|
-
|
|
102
|
-
*Nico Wenterodt*
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
## Rails 7.1.2 (November 10, 2023) ##
|
|
106
|
-
|
|
107
|
-
* No changes.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
## Rails 7.1.1 (October 11, 2023) ##
|
|
111
|
-
|
|
112
|
-
* No changes.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
## Rails 7.1.0 (October 05, 2023) ##
|
|
116
|
-
|
|
117
|
-
* No changes.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
## Rails 7.1.0.rc2 (October 01, 2023) ##
|
|
121
|
-
|
|
122
|
-
* No changes.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
## Rails 7.1.0.rc1 (September 27, 2023) ##
|
|
126
|
-
|
|
127
|
-
* Add `expires_at` option to `ActiveStorage::Blob#signed_id`.
|
|
128
|
-
|
|
129
|
-
```ruby
|
|
130
|
-
rails_blob_path(user.avatar, disposition: "attachment", expires_at: 30.minutes.from_now)
|
|
131
|
-
<%= image_tag rails_blob_path(user.avatar.variant(resize: "100x100"), expires_at: 30.minutes.from_now) %>
|
|
132
|
-
```
|
|
53
|
+
## Rails 8.1.2 (January 08, 2026) ##
|
|
133
54
|
|
|
134
|
-
|
|
55
|
+
* Restore ADC when signing URLs with IAM for GCS
|
|
135
56
|
|
|
136
|
-
|
|
57
|
+
ADC was previously used for automatic authorization when signing URLs with IAM.
|
|
58
|
+
Now it is again, but the auth client is memoized so that new credentials are only
|
|
59
|
+
requested when the current ones expire. Other auth methods can now be used
|
|
60
|
+
instead by setting the authorization on `ActiveStorage::Service::GCSService#iam_client`.
|
|
137
61
|
|
|
138
62
|
```ruby
|
|
139
|
-
|
|
140
|
-
User.create!(avatar: file_fixture("image.jpg"))
|
|
63
|
+
ActiveStorage::Blob.service.iam_client.authorization = Google::Auth::ImpersonatedServiceAccountCredentials.new(options)
|
|
141
64
|
```
|
|
142
65
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
## Rails 7.1.0.beta1 (September 13, 2023) ##
|
|
147
|
-
|
|
148
|
-
* Disables the session in `ActiveStorage::Blobs::ProxyController`
|
|
149
|
-
and `ActiveStorage::Representations::ProxyController`
|
|
150
|
-
in order to allow caching by default in some CDNs as CloudFlare
|
|
151
|
-
|
|
152
|
-
Fixes #44136
|
|
153
|
-
|
|
154
|
-
*Bruno Prieto*
|
|
155
|
-
|
|
156
|
-
* Add `tags` to `ActiveStorage::Analyzer::AudioAnalyzer` output
|
|
66
|
+
This is safer than setting `Google::Apis::RequestOptions.default.authorization`
|
|
67
|
+
because it only applies to Active Storage and does not affect other Google API
|
|
68
|
+
clients.
|
|
157
69
|
|
|
158
|
-
*
|
|
70
|
+
*Justin Malčić*
|
|
159
71
|
|
|
160
|
-
* Add an option to preprocess variants
|
|
161
72
|
|
|
162
|
-
|
|
163
|
-
sometimes we're sure that they are accessed and want to processed them
|
|
164
|
-
upfront.
|
|
73
|
+
## Rails 8.1.1 (October 28, 2025) ##
|
|
165
74
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
```
|
|
169
|
-
class User < ApplicationRecord
|
|
170
|
-
has_one_attached :avatar do |attachable|
|
|
171
|
-
attachable.variant :thumb, resize_to_limit: [100, 100], preprocessed: true
|
|
172
|
-
end
|
|
173
|
-
end
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
*Shouichi Kamiya*
|
|
177
|
-
|
|
178
|
-
* Fix variants not included when eager loading multiple records containing a single attachment
|
|
75
|
+
* No changes.
|
|
179
76
|
|
|
180
|
-
When using the `with_attached_#{name}` scope for a `has_one_attached` relation,
|
|
181
|
-
attachment variants were not eagerly loaded.
|
|
182
77
|
|
|
183
|
-
|
|
78
|
+
## Rails 8.1.0 (October 22, 2025) ##
|
|
184
79
|
|
|
185
|
-
*
|
|
80
|
+
* Add structured events for Active Storage:
|
|
81
|
+
- `active_storage.service_upload`
|
|
82
|
+
- `active_storage.service_download`
|
|
83
|
+
- `active_storage.service_streaming_download`
|
|
84
|
+
- `active_storage.preview`
|
|
85
|
+
- `active_storage.service_delete`
|
|
86
|
+
- `active_storage.service_delete_prefixed`
|
|
87
|
+
- `active_storage.service_exist`
|
|
88
|
+
- `active_storage.service_url`
|
|
89
|
+
- `active_storage.service_mirror`
|
|
186
90
|
|
|
187
|
-
|
|
188
|
-
```ruby
|
|
189
|
-
User.find(params[:id]).update!(avatar: nil)
|
|
190
|
-
```
|
|
91
|
+
*Gannon McGibbon*
|
|
191
92
|
|
|
192
|
-
|
|
193
|
-
empty string would result in an `ActiveSupport::MessageVerifier::InvalidSignature: mismatched digest`
|
|
194
|
-
error being raised, because it's being treated as a signed blob id.
|
|
93
|
+
* Allow analyzers and variant transformer to be fully configurable
|
|
195
94
|
|
|
196
|
-
Now, nil and an empty string are treated as a delete, which allows attachments to be removed via:
|
|
197
95
|
```ruby
|
|
198
|
-
|
|
96
|
+
# ActiveStorage.analyzers can be set to an empty array:
|
|
97
|
+
config.active_storage.analyzers = []
|
|
98
|
+
# => ActiveStorage.analyzers = []
|
|
199
99
|
|
|
100
|
+
# or use custom analyzer:
|
|
101
|
+
config.active_storage.analyzers = [ CustomAnalyzer ]
|
|
102
|
+
# => ActiveStorage.analyzers = [ CustomAnalyzer ]
|
|
200
103
|
```
|
|
201
104
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
* Remove mini_mime usage in favour of marcel.
|
|
205
|
-
|
|
206
|
-
We have two libraries that are have similar usage. This change removes
|
|
207
|
-
dependency on mini_mime and makes use of similar methods from marcel.
|
|
208
|
-
|
|
209
|
-
*Vipul A M*
|
|
105
|
+
If no configuration is provided, it will use the default analyzers.
|
|
210
106
|
|
|
211
|
-
|
|
107
|
+
You can also disable variant processor to remove warnings on startup about missing gems.
|
|
212
108
|
|
|
213
109
|
```ruby
|
|
214
|
-
|
|
110
|
+
config.active_storage.variant_processor = :disabled
|
|
215
111
|
```
|
|
216
112
|
|
|
217
|
-
*
|
|
218
|
-
|
|
219
|
-
* Add `sample_rate` to `ActiveStorage::Analyzer::AudioAnalyzer` output
|
|
220
|
-
|
|
221
|
-
*Matija Čupić*
|
|
222
|
-
|
|
223
|
-
* Remove deprecated `purge` and `purge_later` methods from the attachments association.
|
|
113
|
+
*zzak*, *Alexandre Ruban*
|
|
224
114
|
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
* Remove deprecated behavior when assigning to a collection of attachments.
|
|
228
|
-
|
|
229
|
-
Instead of appending to the collection, the collection is now replaced.
|
|
115
|
+
* Remove deprecated `:azure` storage service.
|
|
230
116
|
|
|
231
117
|
*Rafael Mendonça França*
|
|
232
118
|
|
|
233
|
-
* Remove
|
|
119
|
+
* Remove unnecessary calls to the GCP metadata server.
|
|
234
120
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
* Add missing preview event to `ActiveStorage::LogSubscriber`
|
|
242
|
-
|
|
243
|
-
A `preview` event is being instrumented in `ActiveStorage::Previewer`.
|
|
244
|
-
However it was not added inside ActiveStorage's LogSubscriber class.
|
|
245
|
-
|
|
246
|
-
This will allow to have logs for when a preview happens
|
|
247
|
-
in the same fashion as all other ActiveStorage events such as
|
|
248
|
-
`upload` and `download` inside `Rails.logger`.
|
|
249
|
-
|
|
250
|
-
*Chedli Bourguiba*
|
|
251
|
-
|
|
252
|
-
* Fix retrieving rotation value from FFmpeg on version 5.0+.
|
|
253
|
-
|
|
254
|
-
In FFmpeg version 5.0+ the rotation value has been removed from tags.
|
|
255
|
-
Instead the value can be found in side_data_list. Along with
|
|
256
|
-
this update it's possible to have values of -90, -270 to denote the video
|
|
257
|
-
has been rotated.
|
|
258
|
-
|
|
259
|
-
*Haroon Ahmed*
|
|
260
|
-
|
|
261
|
-
* Touch all corresponding model records after ActiveStorage::Blob is analyzed
|
|
262
|
-
|
|
263
|
-
This fixes a race condition where a record can be requested and have a cache entry built, before
|
|
264
|
-
the initial `analyze_later` completes, which will not be invalidated until something else
|
|
265
|
-
updates the record. This also invalidates cache entries when a blob is re-analyzed, which
|
|
266
|
-
is helpful if a bug is fixed in an analyzer or a new analyzer is added.
|
|
267
|
-
|
|
268
|
-
*Nate Matykiewicz*
|
|
269
|
-
|
|
270
|
-
* Add ability to use pre-defined variants when calling `preview` or
|
|
271
|
-
`representation` on an attachment.
|
|
121
|
+
Calling Google::Auth.get_application_default triggers an explicit call to
|
|
122
|
+
the metadata server - given it was being called for significant number of
|
|
123
|
+
file operations, it can lead to considerable tail latencies and even metadata
|
|
124
|
+
server overloads. Instead, it's preferable (and significantly more efficient)
|
|
125
|
+
that applications use:
|
|
272
126
|
|
|
273
127
|
```ruby
|
|
274
|
-
|
|
275
|
-
has_one_attached :file do |attachable|
|
|
276
|
-
attachable.variant :thumb, resize_to_limit: [100, 100]
|
|
277
|
-
end
|
|
278
|
-
end
|
|
279
|
-
|
|
280
|
-
<%= image_tag user.file.representation(:thumb) %>
|
|
128
|
+
Google::Apis::RequestOptions.default.authorization = Google::Auth.get_application_default(...)
|
|
281
129
|
```
|
|
282
130
|
|
|
283
|
-
|
|
131
|
+
In the cases applications do not set that, the GCP libraries automatically determine credentials.
|
|
284
132
|
|
|
285
|
-
|
|
286
|
-
|
|
133
|
+
This also enables using credentials other than those of the associated GCP
|
|
134
|
+
service account like when using impersonation.
|
|
287
135
|
|
|
288
|
-
*
|
|
136
|
+
*Alex Coomans*
|
|
289
137
|
|
|
290
|
-
*
|
|
138
|
+
* Direct upload progress accounts for server processing time.
|
|
291
139
|
|
|
292
|
-
|
|
293
|
-
```ruby
|
|
294
|
-
ActiveRecord::Base.transaction do
|
|
295
|
-
user.attachments.attach({
|
|
296
|
-
content_type: "text/plain",
|
|
297
|
-
filename: "dummy.txt",
|
|
298
|
-
io: ::StringIO.new("dummy"),
|
|
299
|
-
})
|
|
300
|
-
user.attachments.attach({
|
|
301
|
-
content_type: "text/plain",
|
|
302
|
-
filename: "dummy2.txt",
|
|
303
|
-
io: ::StringIO.new("dummy2"),
|
|
304
|
-
})
|
|
305
|
-
end
|
|
306
|
-
|
|
307
|
-
assert_equal 2, user.attachments.count
|
|
308
|
-
assert user.attachments.first.service.exist?(user.attachments.first.key) # Fails
|
|
309
|
-
```
|
|
140
|
+
*Jeremy Daer*
|
|
310
141
|
|
|
311
|
-
|
|
312
|
-
once the transaction is committed.
|
|
142
|
+
* Delegate `ActiveStorage::Filename#to_str` to `#to_s`
|
|
313
143
|
|
|
314
|
-
|
|
144
|
+
Supports checking String equality:
|
|
315
145
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
is raised with a meaningful message when attempting to use Active Storage.
|
|
323
|
-
|
|
324
|
-
*Ghouse Mohamed*
|
|
325
|
-
|
|
326
|
-
* Fixes proxy downloads of files over 5mb
|
|
327
|
-
|
|
328
|
-
Previously, trying to view and/or download files larger than 5mb stored in
|
|
329
|
-
services like S3 via proxy mode could return corrupted files at around
|
|
330
|
-
5.2mb or cause random halts in the download. Now,
|
|
331
|
-
`ActiveStorage::Blobs::ProxyController` correctly handles streaming these
|
|
332
|
-
larger files from the service to the client without any issues.
|
|
333
|
-
|
|
334
|
-
Fixes #44679
|
|
335
|
-
|
|
336
|
-
*Felipe Raul*
|
|
337
|
-
|
|
338
|
-
* Saving attachment(s) to a record returns the blob/blobs object
|
|
339
|
-
|
|
340
|
-
Previously, saving attachments did not return the blob/blobs that
|
|
341
|
-
were attached. Now, saving attachments to a record with `#attach`
|
|
342
|
-
method returns the blob or array of blobs that were attached to
|
|
343
|
-
the record. If it fails to save the attachment(s), then it returns
|
|
344
|
-
`false`.
|
|
345
|
-
|
|
346
|
-
*Ghouse Mohamed*
|
|
347
|
-
|
|
348
|
-
* Don't stream responses in redirect mode
|
|
146
|
+
```ruby
|
|
147
|
+
filename = ActiveStorage::Filename.new("file.txt")
|
|
148
|
+
filename == "file.txt" # => true
|
|
149
|
+
filename in "file.txt" # => true
|
|
150
|
+
"file.txt" == filename # => true
|
|
151
|
+
```
|
|
349
152
|
|
|
350
|
-
|
|
351
|
-
responses which caused a new thread to be created, and could end
|
|
352
|
-
up leaking connections in the connection pool. But since redirect
|
|
353
|
-
mode doesn't actually send any data, it doesn't need to be
|
|
354
|
-
streamed.
|
|
153
|
+
*Sean Doyle*
|
|
355
154
|
|
|
356
|
-
|
|
155
|
+
* A Blob will no longer autosave associated Attachment.
|
|
357
156
|
|
|
358
|
-
|
|
157
|
+
This fixes an issue where a record with an attachment would have
|
|
158
|
+
its dirty attributes reset, preventing your `after commit` callbacks
|
|
159
|
+
on that record to behave as expected.
|
|
359
160
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
161
|
+
Note that this change doesn't require any changes on your application
|
|
162
|
+
and is supposed to be internal. Active Storage Attachment will continue
|
|
163
|
+
to be autosaved (through a different relation).
|
|
363
164
|
|
|
364
|
-
*
|
|
165
|
+
*Edouard-chin*
|
|
365
166
|
|
|
366
|
-
Please check [
|
|
167
|
+
Please check [8-0-stable](https://github.com/rails/rails/blob/8-0-stable/activestorage/CHANGELOG.md) for previous changes.
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Active Storage
|
|
2
2
|
|
|
3
|
-
Active Storage makes it simple to upload and reference files in cloud services like [Amazon S3](https://aws.amazon.com/s3/), [Google Cloud Storage](https://cloud.google.com/storage/docs/),
|
|
3
|
+
Active Storage makes it simple to upload and reference files in cloud services like [Amazon S3](https://aws.amazon.com/s3/), or [Google Cloud Storage](https://cloud.google.com/storage/docs/), and attach those files to Active Records. Supports having one main service and mirrors in other services for redundancy. It also provides a disk service for testing or local deployments, but the focus is on cloud storage.
|
|
4
4
|
|
|
5
5
|
Files can be uploaded from the server to the cloud or directly from the client to the cloud.
|
|
6
6
|
|
|
@@ -73,7 +73,7 @@ end
|
|
|
73
73
|
```erb
|
|
74
74
|
<%= form_with model: @message, local: true do |form| %>
|
|
75
75
|
<%= form.text_field :title, placeholder: "Title" %><br>
|
|
76
|
-
<%= form.
|
|
76
|
+
<%= form.textarea :content %><br><br>
|
|
77
77
|
|
|
78
78
|
<%= form.file_field :images, multiple: true %><br>
|
|
79
79
|
<%= form.submit %>
|
|
@@ -88,7 +88,7 @@ class MessagesController < ApplicationController
|
|
|
88
88
|
end
|
|
89
89
|
|
|
90
90
|
def create
|
|
91
|
-
message = Message.create! params.
|
|
91
|
+
message = Message.create! params.expect(message: [ :title, :content, images: [] ])
|
|
92
92
|
redirect_to message
|
|
93
93
|
end
|
|
94
94
|
|
|
@@ -173,7 +173,10 @@ Active Storage, with its included JavaScript library, supports uploading directl
|
|
|
173
173
|
```erb
|
|
174
174
|
<%= form.file_field :attachments, multiple: true, direct_upload: true %>
|
|
175
175
|
```
|
|
176
|
-
|
|
176
|
+
|
|
177
|
+
3. Configure CORS on third-party storage services to allow direct upload requests.
|
|
178
|
+
|
|
179
|
+
4. That's it! Uploads begin upon form submission.
|
|
177
180
|
|
|
178
181
|
### Direct upload JavaScript events
|
|
179
182
|
|
|
@@ -203,6 +206,6 @@ Bug reports for the Ruby on \Rails project can be filed here:
|
|
|
203
206
|
|
|
204
207
|
* https://github.com/rails/rails/issues
|
|
205
208
|
|
|
206
|
-
Feature requests should be discussed on the
|
|
209
|
+
Feature requests should be discussed on the rubyonrails-core forum here:
|
|
207
210
|
|
|
208
211
|
* https://discuss.rubyonrails.org/c/rubyonrails-core
|
|
@@ -672,7 +672,7 @@ class DirectUploadController {
|
|
|
672
672
|
}));
|
|
673
673
|
}
|
|
674
674
|
uploadRequestDidProgress(event) {
|
|
675
|
-
const progress = event.loaded / event.total *
|
|
675
|
+
const progress = event.loaded / event.total * 90;
|
|
676
676
|
if (progress) {
|
|
677
677
|
this.dispatch("progress", {
|
|
678
678
|
progress: progress
|
|
@@ -707,6 +707,42 @@ class DirectUploadController {
|
|
|
707
707
|
xhr: xhr
|
|
708
708
|
});
|
|
709
709
|
xhr.upload.addEventListener("progress", (event => this.uploadRequestDidProgress(event)));
|
|
710
|
+
xhr.upload.addEventListener("loadend", (() => {
|
|
711
|
+
this.simulateResponseProgress(xhr);
|
|
712
|
+
}));
|
|
713
|
+
}
|
|
714
|
+
simulateResponseProgress(xhr) {
|
|
715
|
+
let progress = 90;
|
|
716
|
+
const startTime = Date.now();
|
|
717
|
+
const updateProgress = () => {
|
|
718
|
+
const elapsed = Date.now() - startTime;
|
|
719
|
+
const estimatedResponseTime = this.estimateResponseTime();
|
|
720
|
+
const responseProgress = Math.min(elapsed / estimatedResponseTime, 1);
|
|
721
|
+
progress = 90 + responseProgress * 9;
|
|
722
|
+
this.dispatch("progress", {
|
|
723
|
+
progress: progress
|
|
724
|
+
});
|
|
725
|
+
if (xhr.readyState !== XMLHttpRequest.DONE && progress < 99) {
|
|
726
|
+
requestAnimationFrame(updateProgress);
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
xhr.addEventListener("loadend", (() => {
|
|
730
|
+
this.dispatch("progress", {
|
|
731
|
+
progress: 100
|
|
732
|
+
});
|
|
733
|
+
}));
|
|
734
|
+
requestAnimationFrame(updateProgress);
|
|
735
|
+
}
|
|
736
|
+
estimateResponseTime() {
|
|
737
|
+
const fileSize = this.file.size;
|
|
738
|
+
const MB = 1024 * 1024;
|
|
739
|
+
if (fileSize < MB) {
|
|
740
|
+
return 1e3;
|
|
741
|
+
} else if (fileSize < 10 * MB) {
|
|
742
|
+
return 2e3;
|
|
743
|
+
} else {
|
|
744
|
+
return 3e3 + fileSize / MB * 50;
|
|
745
|
+
}
|
|
710
746
|
}
|
|
711
747
|
}
|
|
712
748
|
|
|
@@ -845,4 +881,4 @@ function autostart() {
|
|
|
845
881
|
|
|
846
882
|
setTimeout(autostart, 1);
|
|
847
883
|
|
|
848
|
-
export { DirectUpload, DirectUploadController, DirectUploadsController, start };
|
|
884
|
+
export { DirectUpload, DirectUploadController, DirectUploadsController, dispatchEvent, start };
|
|
@@ -662,7 +662,7 @@
|
|
|
662
662
|
}));
|
|
663
663
|
}
|
|
664
664
|
uploadRequestDidProgress(event) {
|
|
665
|
-
const progress = event.loaded / event.total *
|
|
665
|
+
const progress = event.loaded / event.total * 90;
|
|
666
666
|
if (progress) {
|
|
667
667
|
this.dispatch("progress", {
|
|
668
668
|
progress: progress
|
|
@@ -697,6 +697,42 @@
|
|
|
697
697
|
xhr: xhr
|
|
698
698
|
});
|
|
699
699
|
xhr.upload.addEventListener("progress", (event => this.uploadRequestDidProgress(event)));
|
|
700
|
+
xhr.upload.addEventListener("loadend", (() => {
|
|
701
|
+
this.simulateResponseProgress(xhr);
|
|
702
|
+
}));
|
|
703
|
+
}
|
|
704
|
+
simulateResponseProgress(xhr) {
|
|
705
|
+
let progress = 90;
|
|
706
|
+
const startTime = Date.now();
|
|
707
|
+
const updateProgress = () => {
|
|
708
|
+
const elapsed = Date.now() - startTime;
|
|
709
|
+
const estimatedResponseTime = this.estimateResponseTime();
|
|
710
|
+
const responseProgress = Math.min(elapsed / estimatedResponseTime, 1);
|
|
711
|
+
progress = 90 + responseProgress * 9;
|
|
712
|
+
this.dispatch("progress", {
|
|
713
|
+
progress: progress
|
|
714
|
+
});
|
|
715
|
+
if (xhr.readyState !== XMLHttpRequest.DONE && progress < 99) {
|
|
716
|
+
requestAnimationFrame(updateProgress);
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
xhr.addEventListener("loadend", (() => {
|
|
720
|
+
this.dispatch("progress", {
|
|
721
|
+
progress: 100
|
|
722
|
+
});
|
|
723
|
+
}));
|
|
724
|
+
requestAnimationFrame(updateProgress);
|
|
725
|
+
}
|
|
726
|
+
estimateResponseTime() {
|
|
727
|
+
const fileSize = this.file.size;
|
|
728
|
+
const MB = 1024 * 1024;
|
|
729
|
+
if (fileSize < MB) {
|
|
730
|
+
return 1e3;
|
|
731
|
+
} else if (fileSize < 10 * MB) {
|
|
732
|
+
return 2e3;
|
|
733
|
+
} else {
|
|
734
|
+
return 3e3 + fileSize / MB * 50;
|
|
735
|
+
}
|
|
700
736
|
}
|
|
701
737
|
}
|
|
702
738
|
const inputSelector = "input[type=file][data-direct-upload-url]:not([disabled])";
|
|
@@ -822,6 +858,7 @@
|
|
|
822
858
|
exports.DirectUpload = DirectUpload;
|
|
823
859
|
exports.DirectUploadController = DirectUploadController;
|
|
824
860
|
exports.DirectUploadsController = DirectUploadsController;
|
|
861
|
+
exports.dispatchEvent = dispatchEvent;
|
|
825
862
|
exports.start = start;
|
|
826
863
|
Object.defineProperty(exports, "__esModule", {
|
|
827
864
|
value: true
|
|
@@ -11,7 +11,7 @@ class ActiveStorage::DirectUploadsController < ActiveStorage::BaseController
|
|
|
11
11
|
|
|
12
12
|
private
|
|
13
13
|
def blob_args
|
|
14
|
-
params.
|
|
14
|
+
params.expect(blob: [:filename, :byte_size, :checksum, :content_type, metadata: {}]).to_h.symbolize_keys
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def direct_upload_json(blob)
|
|
@@ -17,6 +17,8 @@ class ActiveStorage::DiskController < ActiveStorage::BaseController
|
|
|
17
17
|
end
|
|
18
18
|
rescue Errno::ENOENT
|
|
19
19
|
head :not_found
|
|
20
|
+
rescue ActiveStorage::InvalidKeyError
|
|
21
|
+
head :not_found
|
|
20
22
|
end
|
|
21
23
|
|
|
22
24
|
def update
|
|
@@ -25,13 +27,15 @@ class ActiveStorage::DiskController < ActiveStorage::BaseController
|
|
|
25
27
|
named_disk_service(token[:service_name]).upload token[:key], request.body, checksum: token[:checksum]
|
|
26
28
|
head :no_content
|
|
27
29
|
else
|
|
28
|
-
head
|
|
30
|
+
head ActionDispatch::Constants::UNPROCESSABLE_CONTENT
|
|
29
31
|
end
|
|
30
32
|
else
|
|
31
33
|
head :not_found
|
|
32
34
|
end
|
|
33
35
|
rescue ActiveStorage::IntegrityError
|
|
34
|
-
head
|
|
36
|
+
head ActionDispatch::Constants::UNPROCESSABLE_CONTENT
|
|
37
|
+
rescue ActiveStorage::InvalidKeyError
|
|
38
|
+
head ActionDispatch::Constants::UNPROCESSABLE_CONTENT
|
|
35
39
|
end
|
|
36
40
|
|
|
37
41
|
private
|