activestorage 7.2.3.1 → 8.0.0.beta1
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 +12 -171
- data/README.md +3 -3
- data/app/assets/javascripts/activestorage.esm.js +1 -1
- data/app/assets/javascripts/activestorage.js +1 -0
- data/app/controllers/active_storage/direct_uploads_controller.rb +1 -1
- data/app/controllers/active_storage/disk_controller.rb +2 -6
- data/app/controllers/concerns/active_storage/streaming.rb +10 -8
- data/app/javascript/activestorage/index.js +2 -1
- data/app/models/active_storage/blob/representable.rb +3 -69
- data/app/models/active_storage/blob.rb +16 -22
- data/app/models/active_storage/filename.rb +1 -1
- data/lib/active_storage/attached/changes/create_one.rb +1 -1
- data/lib/active_storage/attached/model.rb +18 -33
- data/lib/active_storage/engine.rb +1 -5
- data/lib/active_storage/errors.rb +0 -4
- data/lib/active_storage/fixture_set.rb +1 -1
- data/lib/active_storage/gem_version.rb +4 -4
- data/lib/active_storage/service/azure_storage_service.rb +7 -0
- data/lib/active_storage/service/disk_service.rb +2 -46
- data/lib/active_storage/service/mirror_service.rb +12 -3
- data/lib/active_storage/service/s3_service.rb +4 -19
- data/lib/active_storage.rb +3 -14
- metadata +18 -15
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 90eb90580cb0faea4b29eef5ddf0dbc99dd1087158980f968561fe45c2dcb4d4
|
|
4
|
+
data.tar.gz: bdd0ced8684956539dee4f32ea7eacd16744c72e318aebc0bd815adfa72ffe73
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 46465038ab95d65913a9f359af9b21fcb5b3ee9a37877b32eeabd7943de6373625d7ae51a9383099a89bd7d949ee6b5db37c5518656313121d8991b4e52e7667
|
|
7
|
+
data.tar.gz: 4f7590242bb707d2ed0c9b22871f2e00d28f683224341387c739b9771a85ab18e8655e1a913b2bdabb724bb694cb6b997f4b1c2771fa52a2b96ac1882593471a
|
data/CHANGELOG.md
CHANGED
|
@@ -1,182 +1,23 @@
|
|
|
1
|
-
## Rails
|
|
1
|
+
## Rails 8.0.0.beta1 (September 26, 2024) ##
|
|
2
2
|
|
|
3
|
-
*
|
|
3
|
+
* Deprecate `ActiveStorage::Service::AzureStorageService`.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
*zzak*
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
* Improve `ActiveStorage::Filename#sanitized` method to handle special characters more effectively.
|
|
8
|
+
Replace the characters `"*?<>` with `-` if they exist in the Filename to match the Filename convention of Win OS.
|
|
8
9
|
|
|
9
|
-
*
|
|
10
|
+
*Luong Viet Dung(Martin)*
|
|
10
11
|
|
|
11
|
-
|
|
12
|
-
Content ranges that are too big can result in denial of service.
|
|
12
|
+
* Improve InvariableError, UnpreviewableError and UnrepresentableError message.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Include Blob ID and content_type in the messages.
|
|
15
15
|
|
|
16
|
-
*
|
|
16
|
+
*Petrik de Heus*
|
|
17
17
|
|
|
18
|
-
*
|
|
18
|
+
* Mark proxied files as `immutable` in their Cache-Control header
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
*Nate Matykiewicz*
|
|
21
21
|
|
|
22
|
-
*Jean Boussier*
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
`DiskService#path_for` now raises an `InvalidKeyError` when passed keys with dot segments (".",
|
|
27
|
-
".."), or if the resolved path is outside the storage root directory.
|
|
28
|
-
|
|
29
|
-
`#path_for` also now consistently raises `InvalidKeyError` if the key is invalid in any way, for
|
|
30
|
-
example containing null bytes or having an incompatible encoding. Previously, the exception
|
|
31
|
-
raised may have been `ArgumentError` or `Encoding::CompatibilityError`.
|
|
32
|
-
|
|
33
|
-
`DiskController` now explicitly rescues `InvalidKeyError` with appropriate HTTP status codes.
|
|
34
|
-
|
|
35
|
-
[CVE-2026-33195]
|
|
36
|
-
|
|
37
|
-
*Mike Dalessio*
|
|
38
|
-
|
|
39
|
-
* Prevent glob injection in `DiskService#delete_prefixed`.
|
|
40
|
-
|
|
41
|
-
Escape glob metacharacters in the resolved path before passing to `Dir.glob`.
|
|
42
|
-
|
|
43
|
-
Note that this change breaks any existing code that is relying on `delete_prefixed` to expand
|
|
44
|
-
glob metacharacters. This change presumes that is unintended behavior (as other storage services
|
|
45
|
-
do not respect these metacharacters).
|
|
46
|
-
|
|
47
|
-
[CVE-2026-33202]
|
|
48
|
-
|
|
49
|
-
*Mike Dalessio*
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
## Rails 7.2.3 (October 28, 2025) ##
|
|
53
|
-
|
|
54
|
-
* Fix `config.active_storage.touch_attachment_records` to work with eager loading.
|
|
55
|
-
|
|
56
|
-
*fatkodima*
|
|
57
|
-
|
|
58
|
-
* A Blob will no longer autosave associated Attachment.
|
|
59
|
-
|
|
60
|
-
This fixes an issue where a record with an attachment would have
|
|
61
|
-
its dirty attributes reset, preventing your `after commit` callbacks
|
|
62
|
-
on that record to behave as expected.
|
|
63
|
-
|
|
64
|
-
Note that this change doesn't require any changes on your application
|
|
65
|
-
and is supposed to be internal. Active Storage Attachment will continue
|
|
66
|
-
to be autosaved (through a different relation).
|
|
67
|
-
|
|
68
|
-
*Edouard-chin*
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
## Rails 7.2.2.2 (August 13, 2025) ##
|
|
72
|
-
|
|
73
|
-
* Remove dangerous transformations
|
|
74
|
-
|
|
75
|
-
[CVE-2025-24293]
|
|
76
|
-
|
|
77
|
-
*Zack Deveau*
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
## Rails 7.2.2.1 (December 10, 2024) ##
|
|
81
|
-
|
|
82
|
-
* No changes.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
## Rails 7.2.2 (October 30, 2024) ##
|
|
86
|
-
|
|
87
|
-
* No changes.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
## Rails 7.2.1.2 (October 23, 2024) ##
|
|
91
|
-
|
|
92
|
-
* No changes.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
## Rails 7.2.1.1 (October 15, 2024) ##
|
|
96
|
-
|
|
97
|
-
* No changes.
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
## Rails 7.2.1 (August 22, 2024) ##
|
|
101
|
-
|
|
102
|
-
* No changes.
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
## Rails 7.2.0 (August 09, 2024) ##
|
|
106
|
-
|
|
107
|
-
* Remove deprecated `config.active_storage.silence_invalid_content_types_warning`.
|
|
108
|
-
|
|
109
|
-
*Rafael Mendonça França*
|
|
110
|
-
|
|
111
|
-
* Remove deprecated `config.active_storage.replace_on_assign_to_many`.
|
|
112
|
-
|
|
113
|
-
*Rafael Mendonça França*
|
|
114
|
-
|
|
115
|
-
* Add support for custom `key` in `ActiveStorage::Blob#compose`.
|
|
116
|
-
|
|
117
|
-
*Elvin Efendiev*
|
|
118
|
-
|
|
119
|
-
* Add `image/webp` to `config.active_storage.web_image_content_types` when `load_defaults "7.2"`
|
|
120
|
-
is set.
|
|
121
|
-
|
|
122
|
-
*Lewis Buckley*
|
|
123
|
-
|
|
124
|
-
* Fix JSON-encoding of `ActiveStorage::Filename` instances.
|
|
125
|
-
|
|
126
|
-
*Jonathan del Strother*
|
|
127
|
-
|
|
128
|
-
* Fix N+1 query when fetching preview images for non-image assets.
|
|
129
|
-
|
|
130
|
-
*Aaron Patterson & Justin Searls*
|
|
131
|
-
|
|
132
|
-
* Fix all Active Storage database related models to respect
|
|
133
|
-
`ActiveRecord::Base.table_name_prefix` configuration.
|
|
134
|
-
|
|
135
|
-
*Chedli Bourguiba*
|
|
136
|
-
|
|
137
|
-
* Fix `ActiveStorage::Representations::ProxyController` not returning the proper
|
|
138
|
-
preview image variant for previewable files.
|
|
139
|
-
|
|
140
|
-
*Chedli Bourguiba*
|
|
141
|
-
|
|
142
|
-
* Fix `ActiveStorage::Representations::ProxyController` to proxy untracked
|
|
143
|
-
variants.
|
|
144
|
-
|
|
145
|
-
*Chedli Bourguiba*
|
|
146
|
-
|
|
147
|
-
* When using the `preprocessed: true` option, avoid enqueuing transform jobs
|
|
148
|
-
for blobs that are not representable.
|
|
149
|
-
|
|
150
|
-
*Chedli Bourguiba*
|
|
151
|
-
|
|
152
|
-
* Prevent `ActiveStorage::Blob#preview` to generate a variant if an empty variation is passed.
|
|
153
|
-
|
|
154
|
-
Calls to `#url`, `#key` or `#download` will now use the original preview
|
|
155
|
-
image instead of generating a variant with the exact same dimensions.
|
|
156
|
-
|
|
157
|
-
*Chedli Bourguiba*
|
|
158
|
-
|
|
159
|
-
* Process preview image variant when calling `ActiveStorage::Preview#processed`.
|
|
160
|
-
|
|
161
|
-
For example, `attached_pdf.preview(:thumb).processed` will now immediately
|
|
162
|
-
generate the full-sized preview image and the `:thumb` variant of it.
|
|
163
|
-
Previously, the `:thumb` variant would not be generated until a further call
|
|
164
|
-
to e.g. `processed.url`.
|
|
165
|
-
|
|
166
|
-
*Chedli Bourguiba* and *Jonathan Hefner*
|
|
167
|
-
|
|
168
|
-
* Prevent `ActiveRecord::StrictLoadingViolationError` when strict loading is
|
|
169
|
-
enabled and the variant of an Active Storage preview has already been
|
|
170
|
-
processed (for example, by calling `ActiveStorage::Preview#url`).
|
|
171
|
-
|
|
172
|
-
*Jonathan Hefner*
|
|
173
|
-
|
|
174
|
-
* Fix `preprocessed: true` option for named variants of previewable files.
|
|
175
|
-
|
|
176
|
-
*Nico Wenterodt*
|
|
177
|
-
|
|
178
|
-
* Allow accepting `service` as a proc as well in `has_one_attached` and `has_many_attached`.
|
|
179
|
-
|
|
180
|
-
*Yogesh Khater*
|
|
181
|
-
|
|
182
|
-
Please check [7-1-stable](https://github.com/rails/rails/blob/7-1-stable/activestorage/CHANGELOG.md) for previous changes.
|
|
23
|
+
Please check [7-2-stable](https://github.com/rails/rails/blob/7-2-stable/activestorage/CHANGELOG.md) for previous changes.
|
data/README.md
CHANGED
|
@@ -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
|
|
|
@@ -203,6 +203,6 @@ Bug reports for the Ruby on \Rails project can be filed here:
|
|
|
203
203
|
|
|
204
204
|
* https://github.com/rails/rails/issues
|
|
205
205
|
|
|
206
|
-
Feature requests should be discussed on the
|
|
206
|
+
Feature requests should be discussed on the rails-core mailing list here:
|
|
207
207
|
|
|
208
208
|
* https://discuss.rubyonrails.org/c/rubyonrails-core
|
|
@@ -822,6 +822,7 @@
|
|
|
822
822
|
exports.DirectUpload = DirectUpload;
|
|
823
823
|
exports.DirectUploadController = DirectUploadController;
|
|
824
824
|
exports.DirectUploadsController = DirectUploadsController;
|
|
825
|
+
exports.dispatchEvent = dispatchEvent;
|
|
825
826
|
exports.start = start;
|
|
826
827
|
Object.defineProperty(exports, "__esModule", {
|
|
827
828
|
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,8 +17,6 @@ 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
|
|
22
20
|
end
|
|
23
21
|
|
|
24
22
|
def update
|
|
@@ -27,15 +25,13 @@ class ActiveStorage::DiskController < ActiveStorage::BaseController
|
|
|
27
25
|
named_disk_service(token[:service_name]).upload token[:key], request.body, checksum: token[:checksum]
|
|
28
26
|
head :no_content
|
|
29
27
|
else
|
|
30
|
-
head
|
|
28
|
+
head :unprocessable_entity
|
|
31
29
|
end
|
|
32
30
|
else
|
|
33
31
|
head :not_found
|
|
34
32
|
end
|
|
35
33
|
rescue ActiveStorage::IntegrityError
|
|
36
|
-
head
|
|
37
|
-
rescue ActiveStorage::InvalidKeyError
|
|
38
|
-
head ActionDispatch::Constants::UNPROCESSABLE_CONTENT
|
|
34
|
+
head :unprocessable_entity
|
|
39
35
|
end
|
|
40
36
|
|
|
41
37
|
private
|
|
@@ -14,8 +14,7 @@ module ActiveStorage::Streaming
|
|
|
14
14
|
def send_blob_byte_range_data(blob, range_header, disposition: nil)
|
|
15
15
|
ranges = Rack::Utils.get_byte_ranges(range_header, blob.byte_size)
|
|
16
16
|
|
|
17
|
-
return head(:range_not_satisfiable)
|
|
18
|
-
return head(:range_not_satisfiable) if ranges.length > ActiveStorage.streaming_max_ranges
|
|
17
|
+
return head(:range_not_satisfiable) if ranges.blank? || ranges.all?(&:blank?)
|
|
19
18
|
|
|
20
19
|
if ranges.length == 1
|
|
21
20
|
range = ranges.first
|
|
@@ -52,12 +51,6 @@ module ActiveStorage::Streaming
|
|
|
52
51
|
)
|
|
53
52
|
end
|
|
54
53
|
|
|
55
|
-
def ranges_valid?(ranges)
|
|
56
|
-
return false if ranges.blank? || ranges.all?(&:blank?)
|
|
57
|
-
|
|
58
|
-
ranges.sum { |range| range.end - range.begin } < ActiveStorage.streaming_chunk_max_size
|
|
59
|
-
end
|
|
60
|
-
|
|
61
54
|
# Stream the blob from storage directly to the response. The disposition can be controlled by setting +disposition+.
|
|
62
55
|
# The content type and filename is set directly from the +blob+.
|
|
63
56
|
def send_blob_stream(blob, disposition: nil) # :doc:
|
|
@@ -68,6 +61,15 @@ module ActiveStorage::Streaming
|
|
|
68
61
|
blob.download do |chunk|
|
|
69
62
|
stream.write chunk
|
|
70
63
|
end
|
|
64
|
+
rescue ActiveStorage::FileNotFoundError
|
|
65
|
+
expires_now
|
|
66
|
+
head :not_found
|
|
67
|
+
rescue
|
|
68
|
+
# Status and caching headers are already set, but not commited.
|
|
69
|
+
# Change the status to 500 manually.
|
|
70
|
+
expires_now
|
|
71
|
+
head :internal_server_error
|
|
72
|
+
raise
|
|
71
73
|
end
|
|
72
74
|
end
|
|
73
75
|
end
|
|
@@ -2,7 +2,8 @@ import { start } from "./ujs"
|
|
|
2
2
|
import { DirectUpload } from "./direct_upload"
|
|
3
3
|
import { DirectUploadController } from "./direct_upload_controller"
|
|
4
4
|
import { DirectUploadsController } from "./direct_uploads_controller"
|
|
5
|
-
|
|
5
|
+
import { dispatchEvent } from "./helpers"
|
|
6
|
+
export { start, DirectUpload, DirectUploadController, DirectUploadsController, dispatchEvent }
|
|
6
7
|
|
|
7
8
|
function autostart() {
|
|
8
9
|
if (window.ActiveStorage) {
|
|
@@ -31,77 +31,11 @@ module ActiveStorage::Blob::Representable
|
|
|
31
31
|
# Raises ActiveStorage::InvariableError if the variant processor cannot
|
|
32
32
|
# transform the blob. To determine whether a blob is variable, call
|
|
33
33
|
# ActiveStorage::Blob#variable?.
|
|
34
|
-
#
|
|
35
|
-
# ==== Options
|
|
36
|
-
#
|
|
37
|
-
# Options are defined by the {image_processing gem}[https://github.com/janko/image_processing],
|
|
38
|
-
# and depend on which variant processor you are using:
|
|
39
|
-
# {Vips}[https://github.com/janko/image_processing/blob/master/doc/vips.md] or
|
|
40
|
-
# {MiniMagick}[https://github.com/janko/image_processing/blob/master/doc/minimagick.md].
|
|
41
|
-
# However, both variant processors support the following options:
|
|
42
|
-
#
|
|
43
|
-
# [+:resize_to_limit+]
|
|
44
|
-
# Downsizes the image to fit within the specified dimensions while retaining
|
|
45
|
-
# the original aspect ratio. Will only resize the image if it's larger than
|
|
46
|
-
# the specified dimensions.
|
|
47
|
-
#
|
|
48
|
-
# user.avatar.variant(resize_to_limit: [100, 100])
|
|
49
|
-
#
|
|
50
|
-
# [+:resize_to_fit+]
|
|
51
|
-
# Resizes the image to fit within the specified dimensions while retaining
|
|
52
|
-
# the original aspect ratio. Will downsize the image if it's larger than the
|
|
53
|
-
# specified dimensions or upsize if it's smaller.
|
|
54
|
-
#
|
|
55
|
-
# user.avatar.variant(resize_to_fit: [100, 100])
|
|
56
|
-
#
|
|
57
|
-
# [+:resize_to_fill+]
|
|
58
|
-
# Resizes the image to fill the specified dimensions while retaining the
|
|
59
|
-
# original aspect ratio. If necessary, will crop the image in the larger
|
|
60
|
-
# dimension.
|
|
61
|
-
#
|
|
62
|
-
# user.avatar.variant(resize_to_fill: [100, 100])
|
|
63
|
-
#
|
|
64
|
-
# [+:resize_and_pad+]
|
|
65
|
-
# Resizes the image to fit within the specified dimensions while retaining
|
|
66
|
-
# the original aspect ratio. If necessary, will pad the remaining area with
|
|
67
|
-
# transparent color if source image has alpha channel, black otherwise.
|
|
68
|
-
#
|
|
69
|
-
# user.avatar.variant(resize_and_pad: [100, 100])
|
|
70
|
-
#
|
|
71
|
-
# [+:crop+]
|
|
72
|
-
# Extracts an area from an image. The first two arguments are the left and
|
|
73
|
-
# top edges of area to extract, while the last two arguments are the width
|
|
74
|
-
# and height of the area to extract.
|
|
75
|
-
#
|
|
76
|
-
# user.avatar.variant(crop: [20, 50, 300, 300])
|
|
77
|
-
#
|
|
78
|
-
# [+:rotate+]
|
|
79
|
-
# Rotates the image by the specified angle.
|
|
80
|
-
#
|
|
81
|
-
# user.avatar.variant(rotate: 90)
|
|
82
|
-
#
|
|
83
|
-
# Some options, including those listed above, can accept additional
|
|
84
|
-
# processor-specific values which can be passed as a trailing hash:
|
|
85
|
-
#
|
|
86
|
-
# <!-- Vips supports configuring `crop` for many of its transformations -->
|
|
87
|
-
# <%= image_tag user.avatar.variant(resize_to_fill: [100, 100, { crop: :centre }]) %>
|
|
88
|
-
#
|
|
89
|
-
# If migrating an existing application between MiniMagick and Vips, you will
|
|
90
|
-
# need to update processor-specific options:
|
|
91
|
-
#
|
|
92
|
-
# <!-- MiniMagick -->
|
|
93
|
-
# <%= image_tag user.avatar.variant(resize_to_limit: [100, 100], format: :jpeg,
|
|
94
|
-
# sampling_factor: "4:2:0", strip: true, interlace: "JPEG", colorspace: "sRGB", quality: 80) %>
|
|
95
|
-
#
|
|
96
|
-
# <!-- Vips -->
|
|
97
|
-
# <%= image_tag user.avatar.variant(resize_to_limit: [100, 100], format: :jpeg,
|
|
98
|
-
# saver: { subsample_mode: "on", strip: true, interlace: true, quality: 80 }) %>
|
|
99
|
-
#
|
|
100
34
|
def variant(transformations)
|
|
101
35
|
if variable?
|
|
102
36
|
variant_class.new(self, ActiveStorage::Variation.wrap(transformations).default_to(default_variant_transformations))
|
|
103
37
|
else
|
|
104
|
-
raise ActiveStorage::InvariableError
|
|
38
|
+
raise ActiveStorage::InvariableError, "Can't transform blob with ID=#{id} and content_type=#{content_type}"
|
|
105
39
|
end
|
|
106
40
|
end
|
|
107
41
|
|
|
@@ -130,7 +64,7 @@ module ActiveStorage::Blob::Representable
|
|
|
130
64
|
if previewable?
|
|
131
65
|
ActiveStorage::Preview.new(self, transformations)
|
|
132
66
|
else
|
|
133
|
-
raise ActiveStorage::UnpreviewableError
|
|
67
|
+
raise ActiveStorage::UnpreviewableError, "No previewer found for blob with ID=#{id} and content_type=#{content_type}"
|
|
134
68
|
end
|
|
135
69
|
end
|
|
136
70
|
|
|
@@ -155,7 +89,7 @@ module ActiveStorage::Blob::Representable
|
|
|
155
89
|
when variable?
|
|
156
90
|
variant transformations
|
|
157
91
|
else
|
|
158
|
-
raise ActiveStorage::UnrepresentableError
|
|
92
|
+
raise ActiveStorage::UnrepresentableError, "No previewer found and can't transform blob with ID=#{id} and content_type=#{content_type}"
|
|
159
93
|
end
|
|
160
94
|
end
|
|
161
95
|
|
|
@@ -16,18 +16,10 @@
|
|
|
16
16
|
# Blobs are intended to be immutable in as-so-far as their reference to a specific file goes. You're allowed to
|
|
17
17
|
# update a blob's metadata on a subsequent pass, but you should not update the key or change the uploaded file.
|
|
18
18
|
# If you need to create a derivative or otherwise change the blob, simply create a new blob and purge the old one.
|
|
19
|
-
#
|
|
20
|
-
# When using a custom +key+, the value is treated as trusted. Using untrusted user input
|
|
21
|
-
# as the key may result in unexpected behavior.
|
|
22
19
|
class ActiveStorage::Blob < ActiveStorage::Record
|
|
23
20
|
MINIMUM_TOKEN_LENGTH = 28
|
|
24
21
|
|
|
25
22
|
has_secure_token :key, length: MINIMUM_TOKEN_LENGTH
|
|
26
|
-
|
|
27
|
-
# FIXME: these property should never have been stored in the metadata.
|
|
28
|
-
# The blob table should be migrated to have dedicated columns for theses.
|
|
29
|
-
PROTECTED_METADATA = %w(analyzed identified composed)
|
|
30
|
-
private_constant :PROTECTED_METADATA
|
|
31
23
|
store :metadata, accessors: [ :analyzed, :identified, :composed ], coder: ActiveRecord::Coders::JSON
|
|
32
24
|
|
|
33
25
|
class_attribute :services, default: {}
|
|
@@ -37,7 +29,7 @@ class ActiveStorage::Blob < ActiveStorage::Record
|
|
|
37
29
|
# :method:
|
|
38
30
|
#
|
|
39
31
|
# Returns the associated ActiveStorage::Attachment instances.
|
|
40
|
-
has_many :attachments
|
|
32
|
+
has_many :attachments
|
|
41
33
|
|
|
42
34
|
##
|
|
43
35
|
# :singleton-method:
|
|
@@ -79,9 +71,8 @@ class ActiveStorage::Blob < ActiveStorage::Record
|
|
|
79
71
|
end
|
|
80
72
|
|
|
81
73
|
# Works like +find_signed+, but will raise an +ActiveSupport::MessageVerifier::InvalidSignature+
|
|
82
|
-
# exception if the +signed_id+ has either expired, has a purpose mismatch,
|
|
83
|
-
#
|
|
84
|
-
# the valid signed id can't find a record.
|
|
74
|
+
# exception if the +signed_id+ has either expired, has a purpose mismatch, or has been tampered with.
|
|
75
|
+
# It will also raise an +ActiveRecord::RecordNotFound+ exception if the valid signed id can't find a record.
|
|
85
76
|
def find_signed!(id, record: nil, purpose: :blob_id)
|
|
86
77
|
super(id, purpose: purpose)
|
|
87
78
|
end
|
|
@@ -101,9 +92,6 @@ class ActiveStorage::Blob < ActiveStorage::Record
|
|
|
101
92
|
# be saved before the upload begins to prevent the upload clobbering another due to key collisions.
|
|
102
93
|
# When providing a content type, pass <tt>identify: false</tt> to bypass
|
|
103
94
|
# automatic content type inference.
|
|
104
|
-
#
|
|
105
|
-
# The optional +key+ parameter is treated as trusted. Using untrusted user input
|
|
106
|
-
# as the key may result in unexpected behavior.
|
|
107
95
|
def create_and_upload!(key: nil, io:, filename:, content_type: nil, metadata: nil, service_name: nil, identify: true, record: nil)
|
|
108
96
|
create_after_unfurling!(key: key, io: io, filename: filename, content_type: content_type, metadata: metadata, service_name: service_name, identify: identify).tap do |blob|
|
|
109
97
|
blob.upload_without_unfurling(io)
|
|
@@ -116,7 +104,6 @@ class ActiveStorage::Blob < ActiveStorage::Record
|
|
|
116
104
|
# Once the form using the direct upload is submitted, the blob can be associated with the right record using
|
|
117
105
|
# the signed ID.
|
|
118
106
|
def create_before_direct_upload!(key: nil, filename:, byte_size:, checksum:, content_type: nil, metadata: nil, service_name: nil, record: nil)
|
|
119
|
-
metadata = filter_metadata(metadata)
|
|
120
107
|
create! key: key, filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type, metadata: metadata, service_name: service_name
|
|
121
108
|
end
|
|
122
109
|
|
|
@@ -165,14 +152,21 @@ class ActiveStorage::Blob < ActiveStorage::Record
|
|
|
165
152
|
end
|
|
166
153
|
end
|
|
167
154
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
else
|
|
173
|
-
metadata
|
|
155
|
+
def validate_service_configuration(service_name, model_class, association_name) # :nodoc:
|
|
156
|
+
if service_name
|
|
157
|
+
services.fetch(service_name) do
|
|
158
|
+
raise ArgumentError, "Cannot configure service #{service_name.inspect} for #{model_class}##{association_name}"
|
|
174
159
|
end
|
|
160
|
+
else
|
|
161
|
+
validate_global_service_configuration
|
|
175
162
|
end
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def validate_global_service_configuration # :nodoc:
|
|
166
|
+
if connected? && table_exists? && Rails.configuration.active_storage.service.nil?
|
|
167
|
+
raise RuntimeError, "Missing Active Storage service name. Specify Active Storage service name for config.active_storage.service in config/environments/#{Rails.env}.rb"
|
|
168
|
+
end
|
|
169
|
+
end
|
|
176
170
|
end
|
|
177
171
|
|
|
178
172
|
include Analyzable
|
|
@@ -57,7 +57,7 @@ class ActiveStorage::Filename
|
|
|
57
57
|
#
|
|
58
58
|
# Characters considered unsafe for storage (e.g. \, $, and the RTL override character) are replaced with a dash.
|
|
59
59
|
def sanitized
|
|
60
|
-
@filename.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "�").strip.tr("\u{202E}
|
|
60
|
+
@filename.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "�").strip.tr("\u{202E}%$|:;/<>?*\"\t\r\n\\", "-")
|
|
61
61
|
end
|
|
62
62
|
|
|
63
63
|
# Returns the sanitized version of the filename.
|
|
@@ -121,7 +121,7 @@ module ActiveStorage
|
|
|
121
121
|
service_name = record.attachment_reflections[name].options[:service_name]
|
|
122
122
|
if service_name.is_a?(Proc)
|
|
123
123
|
service_name = service_name.call(record)
|
|
124
|
-
|
|
124
|
+
ActiveStorage::Blob.validate_service_configuration(service_name, record.class, name)
|
|
125
125
|
end
|
|
126
126
|
service_name
|
|
127
127
|
end
|
|
@@ -61,18 +61,16 @@ module ActiveStorage
|
|
|
61
61
|
# There is no column defined on the model side, Active Storage takes
|
|
62
62
|
# care of the mapping between your records and the attachment.
|
|
63
63
|
#
|
|
64
|
-
# To avoid N+1 queries, you can include the attached blobs in your query like so:
|
|
65
|
-
#
|
|
66
|
-
# User.with_attached_avatar
|
|
67
|
-
#
|
|
68
64
|
# Under the covers, this relationship is implemented as a +has_one+ association to a
|
|
69
65
|
# ActiveStorage::Attachment record and a +has_one-through+ association to a
|
|
70
66
|
# ActiveStorage::Blob record. These associations are available as +avatar_attachment+
|
|
71
67
|
# and +avatar_blob+. But you shouldn't need to work with these associations directly in
|
|
72
68
|
# most circumstances.
|
|
73
69
|
#
|
|
74
|
-
#
|
|
75
|
-
#
|
|
70
|
+
# Instead, +has_one_attached+ generates an ActiveStorage::Attached::One proxy to
|
|
71
|
+
# provide access to the associations and factory methods, like +attach+:
|
|
72
|
+
#
|
|
73
|
+
# user.avatar.attach(uploaded_file)
|
|
76
74
|
#
|
|
77
75
|
# The +:dependent+ option defaults to +:purge_later+. This means the attachment will be
|
|
78
76
|
# purged (i.e. destroyed) in the background whenever the record is destroyed.
|
|
@@ -92,6 +90,10 @@ module ActiveStorage
|
|
|
92
90
|
# has_one_attached :avatar, service: ->(user) { user.in_europe_region? ? :s3_europe : :s3_usa }
|
|
93
91
|
# end
|
|
94
92
|
#
|
|
93
|
+
# To avoid N+1 queries, you can include the attached blobs in your query like so:
|
|
94
|
+
#
|
|
95
|
+
# User.with_attached_avatar
|
|
96
|
+
#
|
|
95
97
|
# If you need to enable +strict_loading+ to prevent lazy loading of attachment,
|
|
96
98
|
# pass the +:strict_loading+ option. You can do:
|
|
97
99
|
#
|
|
@@ -104,7 +106,7 @@ module ActiveStorage
|
|
|
104
106
|
# <tt>active_storage_attachments.record_type</tt> polymorphic type column of
|
|
105
107
|
# the corresponding rows.
|
|
106
108
|
def has_one_attached(name, dependent: :purge_later, service: nil, strict_loading: false)
|
|
107
|
-
|
|
109
|
+
ActiveStorage::Blob.validate_service_configuration(service, self, name) unless service.is_a?(Proc)
|
|
108
110
|
|
|
109
111
|
generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
|
110
112
|
# frozen_string_literal: true
|
|
@@ -161,18 +163,16 @@ module ActiveStorage
|
|
|
161
163
|
# There are no columns defined on the model side, Active Storage takes
|
|
162
164
|
# care of the mapping between your records and the attachments.
|
|
163
165
|
#
|
|
164
|
-
# To avoid N+1 queries, you can include the attached blobs in your query like so:
|
|
165
|
-
#
|
|
166
|
-
# Gallery.where(user: Current.user).with_attached_photos
|
|
167
|
-
#
|
|
168
166
|
# Under the covers, this relationship is implemented as a +has_many+ association to a
|
|
169
167
|
# ActiveStorage::Attachment record and a +has_many-through+ association to a
|
|
170
168
|
# ActiveStorage::Blob record. These associations are available as +photos_attachments+
|
|
171
169
|
# and +photos_blobs+. But you shouldn't need to work with these associations directly in
|
|
172
170
|
# most circumstances.
|
|
173
171
|
#
|
|
174
|
-
#
|
|
175
|
-
#
|
|
172
|
+
# Instead, +has_many_attached+ generates an ActiveStorage::Attached::Many proxy to
|
|
173
|
+
# provide access to the associations and factory methods, like +attach+:
|
|
174
|
+
#
|
|
175
|
+
# user.photos.attach(uploaded_file)
|
|
176
176
|
#
|
|
177
177
|
# The +:dependent+ option defaults to +:purge_later+. This means the attachments will be
|
|
178
178
|
# purged (i.e. destroyed) in the background whenever the record is destroyed.
|
|
@@ -192,6 +192,10 @@ module ActiveStorage
|
|
|
192
192
|
# has_many_attached :photos, service: ->(gallery) { gallery.personal? ? :personal_s3 : :s3 }
|
|
193
193
|
# end
|
|
194
194
|
#
|
|
195
|
+
# To avoid N+1 queries, you can include the attached blobs in your query like so:
|
|
196
|
+
#
|
|
197
|
+
# Gallery.where(user: Current.user).with_attached_photos
|
|
198
|
+
#
|
|
195
199
|
# If you need to enable +strict_loading+ to prevent lazy loading of attachments,
|
|
196
200
|
# pass the +:strict_loading+ option. You can do:
|
|
197
201
|
#
|
|
@@ -204,7 +208,7 @@ module ActiveStorage
|
|
|
204
208
|
# <tt>active_storage_attachments.record_type</tt> polymorphic type column of
|
|
205
209
|
# the corresponding rows.
|
|
206
210
|
def has_many_attached(name, dependent: :purge_later, service: nil, strict_loading: false)
|
|
207
|
-
|
|
211
|
+
ActiveStorage::Blob.validate_service_configuration(service, self, name) unless service.is_a?(Proc)
|
|
208
212
|
|
|
209
213
|
generated_association_methods.class_eval <<-CODE, __FILE__, __LINE__ + 1
|
|
210
214
|
# frozen_string_literal: true
|
|
@@ -255,25 +259,6 @@ module ActiveStorage
|
|
|
255
259
|
end
|
|
256
260
|
end
|
|
257
261
|
|
|
258
|
-
class << self
|
|
259
|
-
def validate_service_configuration(service_name, model_class, association_name) # :nodoc:
|
|
260
|
-
if service_name
|
|
261
|
-
ActiveStorage::Blob.services.fetch(service_name) do
|
|
262
|
-
raise ArgumentError, "Cannot configure service #{service_name.inspect} for #{model_class}##{association_name}"
|
|
263
|
-
end
|
|
264
|
-
else
|
|
265
|
-
validate_global_service_configuration(model_class)
|
|
266
|
-
end
|
|
267
|
-
end
|
|
268
|
-
|
|
269
|
-
private
|
|
270
|
-
def validate_global_service_configuration(model_class)
|
|
271
|
-
if model_class.connected? && ActiveStorage::Blob.table_exists? && Rails.configuration.active_storage.service.nil?
|
|
272
|
-
raise RuntimeError, "Missing Active Storage service name. Specify Active Storage service name for config.active_storage.service in config/environments/#{Rails.env}.rb"
|
|
273
|
-
end
|
|
274
|
-
end
|
|
275
|
-
end
|
|
276
|
-
|
|
277
262
|
def attachment_changes # :nodoc:
|
|
278
263
|
@attachment_changes ||= {}
|
|
279
264
|
end
|
|
@@ -84,10 +84,6 @@ module ActiveStorage
|
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
initializer "active_storage.configs" do
|
|
87
|
-
config.before_initialize do |app|
|
|
88
|
-
ActiveStorage.touch_attachment_records = app.config.active_storage.touch_attachment_records != false
|
|
89
|
-
end
|
|
90
|
-
|
|
91
87
|
config.after_initialize do |app|
|
|
92
88
|
ActiveStorage.logger = app.config.active_storage.logger || Rails.logger
|
|
93
89
|
ActiveStorage.variant_processor = app.config.active_storage.variant_processor || :mini_magick
|
|
@@ -116,13 +112,13 @@ module ActiveStorage
|
|
|
116
112
|
ActiveStorage.variable_content_types = app.config.active_storage.variable_content_types || []
|
|
117
113
|
ActiveStorage.web_image_content_types = app.config.active_storage.web_image_content_types || []
|
|
118
114
|
ActiveStorage.content_types_to_serve_as_binary = app.config.active_storage.content_types_to_serve_as_binary || []
|
|
115
|
+
ActiveStorage.touch_attachment_records = app.config.active_storage.touch_attachment_records != false
|
|
119
116
|
ActiveStorage.service_urls_expire_in = app.config.active_storage.service_urls_expire_in || 5.minutes
|
|
120
117
|
ActiveStorage.urls_expire_in = app.config.active_storage.urls_expire_in
|
|
121
118
|
ActiveStorage.content_types_allowed_inline = app.config.active_storage.content_types_allowed_inline || []
|
|
122
119
|
ActiveStorage.binary_content_type = app.config.active_storage.binary_content_type || "application/octet-stream"
|
|
123
120
|
ActiveStorage.video_preview_arguments = app.config.active_storage.video_preview_arguments || "-y -vframes 1 -f image2"
|
|
124
121
|
ActiveStorage.track_variants = app.config.active_storage.track_variants || false
|
|
125
|
-
ActiveStorage.streaming_chunk_max_size = app.config.active_storage.streaming_chunk_max_size || 100.megabytes
|
|
126
122
|
end
|
|
127
123
|
end
|
|
128
124
|
|
|
@@ -26,8 +26,4 @@ module ActiveStorage
|
|
|
26
26
|
|
|
27
27
|
# Raised when a Previewer is unable to generate a preview image.
|
|
28
28
|
class PreviewError < Error; end
|
|
29
|
-
|
|
30
|
-
# Raised when a storage key resolves to a path outside the service's root
|
|
31
|
-
# directory, indicating a potential path traversal attack.
|
|
32
|
-
class InvalidKeyError < Error; end
|
|
33
29
|
end
|
|
@@ -50,7 +50,7 @@ module ActiveStorage
|
|
|
50
50
|
# by ActiveSupport::Testing::FileFixtures.file_fixture, and upload
|
|
51
51
|
# the file to the Service
|
|
52
52
|
#
|
|
53
|
-
#
|
|
53
|
+
# === Examples
|
|
54
54
|
#
|
|
55
55
|
# # tests/fixtures/active_storage/blobs.yml
|
|
56
56
|
# second_thumbnail_blob: <%= ActiveStorage::FixtureSet.blob(
|
|
@@ -15,6 +15,13 @@ module ActiveStorage
|
|
|
15
15
|
attr_reader :client, :container, :signer
|
|
16
16
|
|
|
17
17
|
def initialize(storage_account_name:, storage_access_key:, container:, public: false, **options)
|
|
18
|
+
ActiveStorage.deprecator.warn <<~MSG.squish
|
|
19
|
+
`ActiveStorage::Service::AzureStorageService` is deprecated and will be
|
|
20
|
+
removed in Rails 8.1.
|
|
21
|
+
Please try the `azure-blob` gem instead.
|
|
22
|
+
This gem is not maintained by the Rails team, so please test your applications before deploying to production.
|
|
23
|
+
MSG
|
|
24
|
+
|
|
18
25
|
@client = Azure::Storage::Blob::BlobService.create(storage_account_name: storage_account_name, storage_access_key: storage_access_key, **options)
|
|
19
26
|
@signer = Azure::Storage::Common::Core::Auth::SharedAccessSignature.new(storage_account_name, storage_access_key)
|
|
20
27
|
@container = container
|
|
@@ -60,16 +60,7 @@ module ActiveStorage
|
|
|
60
60
|
|
|
61
61
|
def delete_prefixed(prefix)
|
|
62
62
|
instrument :delete_prefixed, prefix: prefix do
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
# File.expand_path (called within path_for) strips trailing slashes.
|
|
66
|
-
# Restore trailing separator if the original prefix had one, so that
|
|
67
|
-
# the glob "prefix/*" matches files inside the directory, not siblings
|
|
68
|
-
# whose names start with the prefix string.
|
|
69
|
-
prefix_path += "/" if prefix.end_with?("/")
|
|
70
|
-
|
|
71
|
-
escaped = escape_glob_metacharacters(prefix_path)
|
|
72
|
-
Dir.glob("#{escaped}*").each do |path|
|
|
63
|
+
Dir.glob(path_for("#{prefix}*")).each do |path|
|
|
73
64
|
FileUtils.rm_rf(path)
|
|
74
65
|
end
|
|
75
66
|
end
|
|
@@ -107,39 +98,8 @@ module ActiveStorage
|
|
|
107
98
|
{ "Content-Type" => content_type }
|
|
108
99
|
end
|
|
109
100
|
|
|
110
|
-
# Every filesystem operation in DiskService resolves paths through this method (or through
|
|
111
|
-
# make_path_for, which delegates here). This is the primary filesystem security check: all
|
|
112
|
-
# path-traversal protection is enforced here. New methods that touch the filesystem MUST use
|
|
113
|
-
# path_for or make_path_for -- never construct paths from +root+ directly.
|
|
114
101
|
def path_for(key) # :nodoc:
|
|
115
|
-
|
|
116
|
-
raise ActiveStorage::InvalidKeyError, "key is blank"
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
# Reject keys with dot segments as defense in depth. This prevents path traversal both outside
|
|
120
|
-
# and within the storage root. The root containment check below is a more fundamental check on
|
|
121
|
-
# path traversal outside of the disk service root.
|
|
122
|
-
begin
|
|
123
|
-
if key.split("/").intersect?(%w[. ..])
|
|
124
|
-
raise ActiveStorage::InvalidKeyError, "key has path traversal segments"
|
|
125
|
-
end
|
|
126
|
-
rescue Encoding::CompatibilityError
|
|
127
|
-
raise ActiveStorage::InvalidKeyError, "key has incompatible encoding"
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
begin
|
|
131
|
-
path = File.expand_path(File.join(root, folder_for(key), key))
|
|
132
|
-
rescue ArgumentError
|
|
133
|
-
# ArgumentError catches null bytes
|
|
134
|
-
raise ActiveStorage::InvalidKeyError, "key is an invalid string"
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
# The resolved path must be inside the root directory.
|
|
138
|
-
unless path.start_with?(File.expand_path(root) + "/")
|
|
139
|
-
raise ActiveStorage::InvalidKeyError, "key is outside of disk service root"
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
path
|
|
102
|
+
File.join root, folder_for(key), key
|
|
143
103
|
end
|
|
144
104
|
|
|
145
105
|
def compose(source_keys, destination_key, **)
|
|
@@ -196,10 +156,6 @@ module ActiveStorage
|
|
|
196
156
|
[ key[0..1], key[2..3] ].join("/")
|
|
197
157
|
end
|
|
198
158
|
|
|
199
|
-
def escape_glob_metacharacters(path)
|
|
200
|
-
path.gsub(/[\[\]*?{}\\]/) { |c| "\\#{c}" }
|
|
201
|
-
end
|
|
202
|
-
|
|
203
159
|
def make_path_for(key)
|
|
204
160
|
path_for(key).tap { |path| FileUtils.mkdir_p File.dirname(path) }
|
|
205
161
|
end
|
|
@@ -30,6 +30,13 @@ module ActiveStorage
|
|
|
30
30
|
|
|
31
31
|
def initialize(primary:, mirrors:)
|
|
32
32
|
@primary, @mirrors = primary, mirrors
|
|
33
|
+
@executor = Concurrent::ThreadPoolExecutor.new(
|
|
34
|
+
min_threads: 1,
|
|
35
|
+
max_threads: mirrors.size,
|
|
36
|
+
max_queue: 0,
|
|
37
|
+
fallback_policy: :caller_runs,
|
|
38
|
+
idle_time: 60
|
|
39
|
+
)
|
|
33
40
|
end
|
|
34
41
|
|
|
35
42
|
# Upload the +io+ to the +key+ specified to all services. The upload to the primary service is done synchronously
|
|
@@ -75,10 +82,12 @@ module ActiveStorage
|
|
|
75
82
|
end
|
|
76
83
|
|
|
77
84
|
def perform_across_services(method, *args)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
tasks = each_service.collect do |service|
|
|
86
|
+
Concurrent::Promise.execute(executor: @executor) do
|
|
87
|
+
service.public_send method, *args
|
|
88
|
+
end
|
|
81
89
|
end
|
|
90
|
+
tasks.each(&:value!)
|
|
82
91
|
end
|
|
83
92
|
end
|
|
84
93
|
end
|
|
@@ -16,7 +16,6 @@ module ActiveStorage
|
|
|
16
16
|
|
|
17
17
|
def initialize(bucket:, upload: {}, public: false, **options)
|
|
18
18
|
@client = Aws::S3::Resource.new(**options)
|
|
19
|
-
@transfer_manager = Aws::S3::TransferManager.new(client: @client.client) if defined?(Aws::S3::TransferManager)
|
|
20
19
|
@bucket = @client.bucket(bucket)
|
|
21
20
|
|
|
22
21
|
@multipart_upload_threshold = upload.delete(:multipart_threshold) || 100.megabytes
|
|
@@ -101,8 +100,7 @@ module ActiveStorage
|
|
|
101
100
|
def compose(source_keys, destination_key, filename: nil, content_type: nil, disposition: nil, custom_metadata: {})
|
|
102
101
|
content_disposition = content_disposition_with(type: disposition, filename: filename) if disposition && filename
|
|
103
102
|
|
|
104
|
-
upload_stream(
|
|
105
|
-
key: destination_key,
|
|
103
|
+
object_for(destination_key).upload_stream(
|
|
106
104
|
content_type: content_type,
|
|
107
105
|
content_disposition: content_disposition,
|
|
108
106
|
part_size: MINIMUM_UPLOAD_PART_SIZE,
|
|
@@ -118,14 +116,6 @@ module ActiveStorage
|
|
|
118
116
|
end
|
|
119
117
|
|
|
120
118
|
private
|
|
121
|
-
def upload_stream(key:, **options, &block)
|
|
122
|
-
if @transfer_manager
|
|
123
|
-
@transfer_manager.upload_stream(key: key, bucket: bucket.name, **options, &block)
|
|
124
|
-
else
|
|
125
|
-
object_for(key).upload_stream(**options, &block)
|
|
126
|
-
end
|
|
127
|
-
end
|
|
128
|
-
|
|
129
119
|
def private_url(key, expires_in:, filename:, disposition:, content_type:, **client_opts)
|
|
130
120
|
object_for(key).presigned_url :get, expires_in: expires_in.to_i,
|
|
131
121
|
response_content_disposition: content_disposition_with(type: disposition, filename: filename),
|
|
@@ -136,6 +126,7 @@ module ActiveStorage
|
|
|
136
126
|
object_for(key).public_url(**client_opts)
|
|
137
127
|
end
|
|
138
128
|
|
|
129
|
+
|
|
139
130
|
MAXIMUM_UPLOAD_PARTS_COUNT = 10000
|
|
140
131
|
MINIMUM_UPLOAD_PART_SIZE = 5.megabytes
|
|
141
132
|
|
|
@@ -148,18 +139,12 @@ module ActiveStorage
|
|
|
148
139
|
def upload_with_multipart(key, io, content_type: nil, content_disposition: nil, custom_metadata: {})
|
|
149
140
|
part_size = [ io.size.fdiv(MAXIMUM_UPLOAD_PARTS_COUNT).ceil, MINIMUM_UPLOAD_PART_SIZE ].max
|
|
150
141
|
|
|
151
|
-
upload_stream(
|
|
152
|
-
key: key,
|
|
153
|
-
content_type: content_type,
|
|
154
|
-
content_disposition: content_disposition,
|
|
155
|
-
part_size: part_size,
|
|
156
|
-
metadata: custom_metadata,
|
|
157
|
-
**upload_options
|
|
158
|
-
) do |out|
|
|
142
|
+
object_for(key).upload_stream(content_type: content_type, content_disposition: content_disposition, part_size: part_size, metadata: custom_metadata, **upload_options) do |out|
|
|
159
143
|
IO.copy_stream(io, out)
|
|
160
144
|
end
|
|
161
145
|
end
|
|
162
146
|
|
|
147
|
+
|
|
163
148
|
def object_for(key)
|
|
164
149
|
bucket.object(key)
|
|
165
150
|
end
|
data/lib/active_storage.rb
CHANGED
|
@@ -27,7 +27,6 @@ require "active_record"
|
|
|
27
27
|
require "active_support"
|
|
28
28
|
require "active_support/rails"
|
|
29
29
|
require "active_support/core_ext/numeric/time"
|
|
30
|
-
require "active_support/core_ext/numeric/bytes"
|
|
31
30
|
|
|
32
31
|
require "active_storage/version"
|
|
33
32
|
require "active_storage/deprecator"
|
|
@@ -73,6 +72,7 @@ module ActiveStorage
|
|
|
73
72
|
"annotate",
|
|
74
73
|
"antialias",
|
|
75
74
|
"append",
|
|
75
|
+
"apply",
|
|
76
76
|
"attenuate",
|
|
77
77
|
"authenticate",
|
|
78
78
|
"auto_gamma",
|
|
@@ -213,6 +213,7 @@ module ActiveStorage
|
|
|
213
213
|
"linewidth",
|
|
214
214
|
"liquid_rescale",
|
|
215
215
|
"list",
|
|
216
|
+
"loader",
|
|
216
217
|
"log",
|
|
217
218
|
"loop",
|
|
218
219
|
"lowlight_color",
|
|
@@ -275,6 +276,7 @@ module ActiveStorage
|
|
|
275
276
|
"rotate",
|
|
276
277
|
"sample",
|
|
277
278
|
"sampling_factor",
|
|
279
|
+
"saver",
|
|
278
280
|
"scale",
|
|
279
281
|
"scene",
|
|
280
282
|
"screen",
|
|
@@ -351,7 +353,6 @@ module ActiveStorage
|
|
|
351
353
|
]
|
|
352
354
|
mattr_accessor :unsupported_image_processing_arguments
|
|
353
355
|
|
|
354
|
-
mattr_accessor :streaming_chunk_max_size, default: 100.megabytes
|
|
355
356
|
mattr_accessor :service_urls_expire_in, default: 5.minutes
|
|
356
357
|
mattr_accessor :touch_attachment_records, default: true
|
|
357
358
|
mattr_accessor :urls_expire_in
|
|
@@ -362,18 +363,6 @@ module ActiveStorage
|
|
|
362
363
|
|
|
363
364
|
mattr_accessor :track_variants, default: false
|
|
364
365
|
|
|
365
|
-
singleton_class.attr_accessor :checksum_implementation
|
|
366
|
-
@checksum_implementation = OpenSSL::Digest::MD5
|
|
367
|
-
begin
|
|
368
|
-
@checksum_implementation.hexdigest("test")
|
|
369
|
-
rescue # OpenSSL may have MD5 disabled
|
|
370
|
-
require "digest/md5"
|
|
371
|
-
@checksum_implementation = Digest::MD5
|
|
372
|
-
end
|
|
373
|
-
|
|
374
|
-
singleton_class.attr_accessor :streaming_max_ranges
|
|
375
|
-
@streaming_max_ranges = 1
|
|
376
|
-
|
|
377
366
|
mattr_accessor :video_preview_arguments, default: "-y -vframes 1 -f image2"
|
|
378
367
|
|
|
379
368
|
module Transformers
|
metadata
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activestorage
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 8.0.0.beta1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- David Heinemeier Hansson
|
|
8
|
+
autorequire:
|
|
8
9
|
bindir: bin
|
|
9
10
|
cert_chain: []
|
|
10
|
-
date:
|
|
11
|
+
date: 2024-09-26 00:00:00.000000000 Z
|
|
11
12
|
dependencies:
|
|
12
13
|
- !ruby/object:Gem::Dependency
|
|
13
14
|
name: activesupport
|
|
@@ -15,56 +16,56 @@ dependencies:
|
|
|
15
16
|
requirements:
|
|
16
17
|
- - '='
|
|
17
18
|
- !ruby/object:Gem::Version
|
|
18
|
-
version:
|
|
19
|
+
version: 8.0.0.beta1
|
|
19
20
|
type: :runtime
|
|
20
21
|
prerelease: false
|
|
21
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
23
|
requirements:
|
|
23
24
|
- - '='
|
|
24
25
|
- !ruby/object:Gem::Version
|
|
25
|
-
version:
|
|
26
|
+
version: 8.0.0.beta1
|
|
26
27
|
- !ruby/object:Gem::Dependency
|
|
27
28
|
name: actionpack
|
|
28
29
|
requirement: !ruby/object:Gem::Requirement
|
|
29
30
|
requirements:
|
|
30
31
|
- - '='
|
|
31
32
|
- !ruby/object:Gem::Version
|
|
32
|
-
version:
|
|
33
|
+
version: 8.0.0.beta1
|
|
33
34
|
type: :runtime
|
|
34
35
|
prerelease: false
|
|
35
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
36
37
|
requirements:
|
|
37
38
|
- - '='
|
|
38
39
|
- !ruby/object:Gem::Version
|
|
39
|
-
version:
|
|
40
|
+
version: 8.0.0.beta1
|
|
40
41
|
- !ruby/object:Gem::Dependency
|
|
41
42
|
name: activejob
|
|
42
43
|
requirement: !ruby/object:Gem::Requirement
|
|
43
44
|
requirements:
|
|
44
45
|
- - '='
|
|
45
46
|
- !ruby/object:Gem::Version
|
|
46
|
-
version:
|
|
47
|
+
version: 8.0.0.beta1
|
|
47
48
|
type: :runtime
|
|
48
49
|
prerelease: false
|
|
49
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
51
|
requirements:
|
|
51
52
|
- - '='
|
|
52
53
|
- !ruby/object:Gem::Version
|
|
53
|
-
version:
|
|
54
|
+
version: 8.0.0.beta1
|
|
54
55
|
- !ruby/object:Gem::Dependency
|
|
55
56
|
name: activerecord
|
|
56
57
|
requirement: !ruby/object:Gem::Requirement
|
|
57
58
|
requirements:
|
|
58
59
|
- - '='
|
|
59
60
|
- !ruby/object:Gem::Version
|
|
60
|
-
version:
|
|
61
|
+
version: 8.0.0.beta1
|
|
61
62
|
type: :runtime
|
|
62
63
|
prerelease: false
|
|
63
64
|
version_requirements: !ruby/object:Gem::Requirement
|
|
64
65
|
requirements:
|
|
65
66
|
- - '='
|
|
66
67
|
- !ruby/object:Gem::Version
|
|
67
|
-
version:
|
|
68
|
+
version: 8.0.0.beta1
|
|
68
69
|
- !ruby/object:Gem::Dependency
|
|
69
70
|
name: marcel
|
|
70
71
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -189,11 +190,12 @@ licenses:
|
|
|
189
190
|
- MIT
|
|
190
191
|
metadata:
|
|
191
192
|
bug_tracker_uri: https://github.com/rails/rails/issues
|
|
192
|
-
changelog_uri: https://github.com/rails/rails/blob/
|
|
193
|
-
documentation_uri: https://api.rubyonrails.org/
|
|
193
|
+
changelog_uri: https://github.com/rails/rails/blob/v8.0.0.beta1/activestorage/CHANGELOG.md
|
|
194
|
+
documentation_uri: https://api.rubyonrails.org/v8.0.0.beta1/
|
|
194
195
|
mailing_list_uri: https://discuss.rubyonrails.org/c/rubyonrails-talk
|
|
195
|
-
source_code_uri: https://github.com/rails/rails/tree/
|
|
196
|
+
source_code_uri: https://github.com/rails/rails/tree/v8.0.0.beta1/activestorage
|
|
196
197
|
rubygems_mfa_required: 'true'
|
|
198
|
+
post_install_message:
|
|
197
199
|
rdoc_options: []
|
|
198
200
|
require_paths:
|
|
199
201
|
- lib
|
|
@@ -201,14 +203,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
201
203
|
requirements:
|
|
202
204
|
- - ">="
|
|
203
205
|
- !ruby/object:Gem::Version
|
|
204
|
-
version: 3.
|
|
206
|
+
version: 3.2.0
|
|
205
207
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
206
208
|
requirements:
|
|
207
209
|
- - ">="
|
|
208
210
|
- !ruby/object:Gem::Version
|
|
209
211
|
version: '0'
|
|
210
212
|
requirements: []
|
|
211
|
-
rubygems_version:
|
|
213
|
+
rubygems_version: 3.5.16
|
|
214
|
+
signing_key:
|
|
212
215
|
specification_version: 4
|
|
213
216
|
summary: Local and cloud file storage framework.
|
|
214
217
|
test_files: []
|