activestorage 8.0.2.1 → 8.1.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 +40 -54
- data/README.md +6 -3
- data/app/assets/javascripts/activestorage.esm.js +37 -1
- data/app/assets/javascripts/activestorage.js +37 -1
- data/app/controllers/active_storage/disk_controller.rb +2 -2
- data/app/javascript/activestorage/direct_upload_controller.js +48 -1
- data/app/models/active_storage/blob/representable.rb +66 -0
- data/app/models/active_storage/blob.rb +1 -1
- data/app/models/active_storage/filename.rb +1 -0
- data/app/models/active_storage/variant.rb +7 -7
- data/app/models/active_storage/variation.rb +1 -1
- data/lib/active_storage/analyzer/image_analyzer/image_magick.rb +7 -11
- data/lib/active_storage/analyzer/image_analyzer/vips.rb +10 -11
- data/lib/active_storage/analyzer/image_analyzer.rb +5 -0
- data/lib/active_storage/attached/model.rb +4 -4
- data/lib/active_storage/attached.rb +0 -1
- data/lib/active_storage/downloader.rb +1 -1
- data/lib/active_storage/engine.rb +48 -10
- data/lib/active_storage/fixture_set.rb +1 -1
- data/lib/active_storage/gem_version.rb +3 -3
- data/lib/active_storage/service/configurator.rb +6 -0
- data/lib/active_storage/service/disk_service.rb +1 -1
- data/lib/active_storage/service/gcs_service.rb +10 -2
- data/lib/active_storage/service/mirror_service.rb +1 -1
- 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 +4 -1
- data/lib/active_storage/transformers/image_magick.rb +72 -0
- data/lib/active_storage/transformers/image_processing_transformer.rb +5 -67
- data/lib/active_storage/transformers/vips.rb +11 -0
- data/lib/active_storage.rb +13 -0
- metadata +14 -13
- data/lib/active_storage/service/azure_storage_service.rb +0 -201
@@ -1,201 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
gem "azure-storage-blob", ">= 2.0"
|
4
|
-
|
5
|
-
require "active_support/core_ext/numeric/bytes"
|
6
|
-
require "azure/storage/blob"
|
7
|
-
require "azure/storage/common/core/auth/shared_access_signature"
|
8
|
-
|
9
|
-
module ActiveStorage
|
10
|
-
# = Active Storage \Azure Storage \Service
|
11
|
-
#
|
12
|
-
# Wraps the Microsoft Azure Storage Blob Service as an Active Storage service.
|
13
|
-
# See ActiveStorage::Service for the generic API documentation that applies to all services.
|
14
|
-
class Service::AzureStorageService < Service
|
15
|
-
attr_reader :client, :container, :signer
|
16
|
-
|
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
|
-
|
25
|
-
@client = Azure::Storage::Blob::BlobService.create(storage_account_name: storage_account_name, storage_access_key: storage_access_key, **options)
|
26
|
-
@signer = Azure::Storage::Common::Core::Auth::SharedAccessSignature.new(storage_account_name, storage_access_key)
|
27
|
-
@container = container
|
28
|
-
@public = public
|
29
|
-
end
|
30
|
-
|
31
|
-
def upload(key, io, checksum: nil, filename: nil, content_type: nil, disposition: nil, custom_metadata: {}, **)
|
32
|
-
instrument :upload, key: key, checksum: checksum do
|
33
|
-
handle_errors do
|
34
|
-
content_disposition = content_disposition_with(filename: filename, type: disposition) if disposition && filename
|
35
|
-
|
36
|
-
client.create_block_blob(container, key, IO.try_convert(io) || io, content_md5: checksum, content_type: content_type, content_disposition: content_disposition, metadata: custom_metadata)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
def download(key, &block)
|
42
|
-
if block_given?
|
43
|
-
instrument :streaming_download, key: key do
|
44
|
-
stream(key, &block)
|
45
|
-
end
|
46
|
-
else
|
47
|
-
instrument :download, key: key do
|
48
|
-
handle_errors do
|
49
|
-
_, io = client.get_blob(container, key)
|
50
|
-
io.force_encoding(Encoding::BINARY)
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def download_chunk(key, range)
|
57
|
-
instrument :download_chunk, key: key, range: range do
|
58
|
-
handle_errors do
|
59
|
-
_, io = client.get_blob(container, key, start_range: range.begin, end_range: range.exclude_end? ? range.end - 1 : range.end)
|
60
|
-
io.force_encoding(Encoding::BINARY)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
def delete(key)
|
66
|
-
instrument :delete, key: key do
|
67
|
-
client.delete_blob(container, key)
|
68
|
-
rescue Azure::Core::Http::HTTPError => e
|
69
|
-
raise unless e.type == "BlobNotFound"
|
70
|
-
# Ignore files already deleted
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
def delete_prefixed(prefix)
|
75
|
-
instrument :delete_prefixed, prefix: prefix do
|
76
|
-
marker = nil
|
77
|
-
|
78
|
-
loop do
|
79
|
-
results = client.list_blobs(container, prefix: prefix, marker: marker)
|
80
|
-
|
81
|
-
results.each do |blob|
|
82
|
-
client.delete_blob(container, blob.name)
|
83
|
-
end
|
84
|
-
|
85
|
-
break unless marker = results.continuation_token.presence
|
86
|
-
end
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def exist?(key)
|
91
|
-
instrument :exist, key: key do |payload|
|
92
|
-
answer = blob_for(key).present?
|
93
|
-
payload[:exist] = answer
|
94
|
-
answer
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:, custom_metadata: {})
|
99
|
-
instrument :url, key: key do |payload|
|
100
|
-
generated_url = signer.signed_uri(
|
101
|
-
uri_for(key), false,
|
102
|
-
service: "b",
|
103
|
-
permissions: "rw",
|
104
|
-
expiry: format_expiry(expires_in)
|
105
|
-
).to_s
|
106
|
-
|
107
|
-
payload[:url] = generated_url
|
108
|
-
|
109
|
-
generated_url
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def headers_for_direct_upload(key, content_type:, checksum:, filename: nil, disposition: nil, custom_metadata: {}, **)
|
114
|
-
content_disposition = content_disposition_with(type: disposition, filename: filename) if filename
|
115
|
-
|
116
|
-
{ "Content-Type" => content_type, "Content-MD5" => checksum, "x-ms-blob-content-disposition" => content_disposition, "x-ms-blob-type" => "BlockBlob", **custom_metadata_headers(custom_metadata) }
|
117
|
-
end
|
118
|
-
|
119
|
-
def compose(source_keys, destination_key, filename: nil, content_type: nil, disposition: nil, custom_metadata: {})
|
120
|
-
content_disposition = content_disposition_with(type: disposition, filename: filename) if disposition && filename
|
121
|
-
|
122
|
-
client.create_append_blob(
|
123
|
-
container,
|
124
|
-
destination_key,
|
125
|
-
content_type: content_type,
|
126
|
-
content_disposition: content_disposition,
|
127
|
-
metadata: custom_metadata,
|
128
|
-
).tap do |blob|
|
129
|
-
source_keys.each do |source_key|
|
130
|
-
stream(source_key) do |chunk|
|
131
|
-
client.append_blob_block(container, blob.name, chunk)
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
private
|
138
|
-
def private_url(key, expires_in:, filename:, disposition:, content_type:, **)
|
139
|
-
signer.signed_uri(
|
140
|
-
uri_for(key), false,
|
141
|
-
service: "b",
|
142
|
-
permissions: "r",
|
143
|
-
expiry: format_expiry(expires_in),
|
144
|
-
content_disposition: content_disposition_with(type: disposition, filename: filename),
|
145
|
-
content_type: content_type
|
146
|
-
).to_s
|
147
|
-
end
|
148
|
-
|
149
|
-
def public_url(key, **)
|
150
|
-
uri_for(key).to_s
|
151
|
-
end
|
152
|
-
|
153
|
-
|
154
|
-
def uri_for(key)
|
155
|
-
client.generate_uri("#{container}/#{key}")
|
156
|
-
end
|
157
|
-
|
158
|
-
def blob_for(key)
|
159
|
-
client.get_blob_properties(container, key)
|
160
|
-
rescue Azure::Core::Http::HTTPError
|
161
|
-
false
|
162
|
-
end
|
163
|
-
|
164
|
-
def format_expiry(expires_in)
|
165
|
-
expires_in ? Time.now.utc.advance(seconds: expires_in).iso8601 : nil
|
166
|
-
end
|
167
|
-
|
168
|
-
# Reads the object for the given key in chunks, yielding each to the block.
|
169
|
-
def stream(key)
|
170
|
-
blob = blob_for(key)
|
171
|
-
|
172
|
-
chunk_size = 5.megabytes
|
173
|
-
offset = 0
|
174
|
-
|
175
|
-
raise ActiveStorage::FileNotFoundError unless blob.present?
|
176
|
-
|
177
|
-
while offset < blob.properties[:content_length]
|
178
|
-
_, chunk = client.get_blob(container, key, start_range: offset, end_range: offset + chunk_size - 1)
|
179
|
-
yield chunk.force_encoding(Encoding::BINARY)
|
180
|
-
offset += chunk_size
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
def handle_errors
|
185
|
-
yield
|
186
|
-
rescue Azure::Core::Http::HTTPError => e
|
187
|
-
case e.type
|
188
|
-
when "BlobNotFound"
|
189
|
-
raise ActiveStorage::FileNotFoundError
|
190
|
-
when "Md5Mismatch"
|
191
|
-
raise ActiveStorage::IntegrityError
|
192
|
-
else
|
193
|
-
raise
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
def custom_metadata_headers(metadata)
|
198
|
-
metadata.transform_keys { |key| "x-ms-meta-#{key}" }
|
199
|
-
end
|
200
|
-
end
|
201
|
-
end
|