diff options
Diffstat (limited to 'activestorage/app/models')
-rw-r--r-- | activestorage/app/models/active_storage/attachment.rb | 5 | ||||
-rw-r--r-- | activestorage/app/models/active_storage/blob.rb | 48 | ||||
-rw-r--r-- | activestorage/app/models/active_storage/blob/identifiable.rb | 9 |
3 files changed, 51 insertions, 11 deletions
diff --git a/activestorage/app/models/active_storage/attachment.rb b/activestorage/app/models/active_storage/attachment.rb index 4bdd1c0224..13758d9179 100644 --- a/activestorage/app/models/active_storage/attachment.rb +++ b/activestorage/app/models/active_storage/attachment.rb @@ -3,9 +3,8 @@ require "active_support/core_ext/module/delegation" # Attachments associate records with blobs. Usually that's a one record-many blobs relationship, -# but it is possible to associate many different records with the same blob. If you're doing that, -# you'll want to declare with <tt>has_one/many_attached :thingy, dependent: false</tt>, so that destroying -# any one record won't destroy the blob as well. (Then you'll need to do your own garbage collecting, though). +# but it is possible to associate many different records with the same blob. A foreign-key constraint +# on the attachments table prevents blobs from being purged if they’re still attached to any records. class ActiveStorage::Attachment < ActiveRecord::Base self.table_name = "active_storage_attachments" diff --git a/activestorage/app/models/active_storage/blob.rb b/activestorage/app/models/active_storage/blob.rb index 53aa9f0237..6ca7d49bc1 100644 --- a/activestorage/app/models/active_storage/blob.rb +++ b/activestorage/app/models/active_storage/blob.rb @@ -40,7 +40,7 @@ class ActiveStorage::Blob < ActiveRecord::Base end class << self - # You can used the signed ID of a blob to refer to it on the client side without fear of tampering. + # You can use the signed ID of a blob to refer to it on the client side without fear of tampering. # This is particularly helpful for direct uploads where the client-side needs to refer to the blob # that was created ahead of the upload itself on form submission. # @@ -79,6 +79,15 @@ class ActiveStorage::Blob < ActiveRecord::Base def create_before_direct_upload!(filename:, byte_size:, checksum:, content_type: nil, metadata: nil) create! filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type, metadata: metadata end + + # To prevent problems with case-insensitive filesystems, especially in combination + # with databases which treat indices as case-sensitive, all blob keys generated are going + # to only contain the base-36 character alphabet and will therefore be lowercase. To maintain + # the same or higher amount of entropy as in the base-58 encoding used by `has_secure_token` + # the number of bytes used is increased to 28 from the standard 24 + def generate_unique_secure_token + SecureRandom.base36(28) + end end # Returns a signed ID for this blob that's suitable for reference on the client-side without fear of tampering. @@ -87,9 +96,10 @@ class ActiveStorage::Blob < ActiveRecord::Base ActiveStorage.verifier.generate(id, purpose: :blob_id) end - # Returns the key pointing to the file on the service that's associated with this blob. The key is in the - # standard secure-token format from Rails. So it'll look like: XTAPjJCJiuDrLk3TmwyJGpUo. This key is not intended - # to be revealed directly to the user. Always refer to blobs using the signed_id or a verified form of the key. + # Returns the key pointing to the file on the service that's associated with this blob. The key is the + # secure-token format from Rails in lower case. So it'll look like: xtapjjcjiudrlk3tmwyjgpuobabd. + # This key is not intended to be revealed directly to the user. + # Always refer to blobs using the signed_id or a verified form of the key. def key # We can't wait until the record is first saved to have a key for it self[:key] ||= self.class.generate_unique_secure_token @@ -130,8 +140,8 @@ class ActiveStorage::Blob < ActiveRecord::Base def service_url(expires_in: ActiveStorage.service_urls_expire_in, disposition: :inline, filename: nil, **options) filename = ActiveStorage::Filename.wrap(filename || self.filename) - service.url key, expires_in: expires_in, filename: filename, content_type: content_type, - disposition: forcibly_serve_as_binary? ? :attachment : disposition, **options + service.url key, expires_in: expires_in, filename: filename, content_type: content_type_for_service_url, + disposition: forced_disposition_for_service_url || disposition, **options end # Returns a URL that can be used to directly upload a file for this blob on the service. This URL is intended to be @@ -170,7 +180,7 @@ class ActiveStorage::Blob < ActiveRecord::Base end def upload_without_unfurling(io) #:nodoc: - service.upload key, io, checksum: checksum + service.upload key, io, checksum: checksum, **service_metadata end # Downloads the file associated with this blob. If no block is given, the entire file is read into memory and returned. @@ -239,5 +249,29 @@ class ActiveStorage::Blob < ActiveRecord::Base ActiveStorage.content_types_to_serve_as_binary.include?(content_type) end + def allowed_inline? + ActiveStorage.content_types_allowed_inline.include?(content_type) + end + + def content_type_for_service_url + forcibly_serve_as_binary? ? ActiveStorage.binary_content_type : content_type + end + + def forced_disposition_for_service_url + if forcibly_serve_as_binary? || !allowed_inline? + :attachment + end + end + + def service_metadata + if forcibly_serve_as_binary? + { content_type: ActiveStorage.binary_content_type, disposition: :attachment, filename: filename } + elsif !allowed_inline? + { content_type: content_type, disposition: :attachment, filename: filename } + else + { content_type: content_type } + end + end + ActiveSupport.run_load_hooks(:active_storage_blob, self) end diff --git a/activestorage/app/models/active_storage/blob/identifiable.rb b/activestorage/app/models/active_storage/blob/identifiable.rb index 2c17ddc25f..924bd06131 100644 --- a/activestorage/app/models/active_storage/blob/identifiable.rb +++ b/activestorage/app/models/active_storage/blob/identifiable.rb @@ -2,7 +2,10 @@ module ActiveStorage::Blob::Identifiable def identify - update! content_type: identify_content_type, identified: true unless identified? + unless identified? + update! content_type: identify_content_type, identified: true + update_service_metadata + end end def identified? @@ -21,4 +24,8 @@ module ActiveStorage::Blob::Identifiable "" end end + + def update_service_metadata + service.update_metadata key, service_metadata if service_metadata.any? + end end |