From c2ba530c43244b5b60fd629f61cd8b44c43ecda9 Mon Sep 17 00:00:00 2001 From: George Claghorn Date: Mon, 15 Jan 2018 13:06:17 -0500 Subject: Extract content types from blob data --- .../app/models/active_storage/attachment.rb | 6 +++- activestorage/app/models/active_storage/blob.rb | 14 +++++--- .../app/models/active_storage/blob/identifiable.rb | 11 +++++++ .../app/models/active_storage/identification.rb | 38 ++++++++++++++++++++++ 4 files changed, 64 insertions(+), 5 deletions(-) create mode 100644 activestorage/app/models/active_storage/blob/identifiable.rb create mode 100644 activestorage/app/models/active_storage/identification.rb (limited to 'activestorage/app') diff --git a/activestorage/app/models/active_storage/attachment.rb b/activestorage/app/models/active_storage/attachment.rb index 9f61a5dbf3..19f48c57d6 100644 --- a/activestorage/app/models/active_storage/attachment.rb +++ b/activestorage/app/models/active_storage/attachment.rb @@ -14,7 +14,7 @@ class ActiveStorage::Attachment < ActiveRecord::Base delegate_missing_to :blob - after_create_commit :analyze_blob_later + after_create_commit :identify_blob, :analyze_blob_later # Synchronously purges the blob (deletes it from the configured service) and destroys the attachment. def purge @@ -29,6 +29,10 @@ class ActiveStorage::Attachment < ActiveRecord::Base end private + def identify_blob + blob.identify + end + def analyze_blob_later blob.analyze_later unless blob.analyzed? end diff --git a/activestorage/app/models/active_storage/blob.rb b/activestorage/app/models/active_storage/blob.rb index 2ac09d3062..7067c58259 100644 --- a/activestorage/app/models/active_storage/blob.rb +++ b/activestorage/app/models/active_storage/blob.rb @@ -14,12 +14,12 @@ # update a blob's metadata on a subsequent pass, but you should not update the key or change the uploaded file. # If you need to create a derivative or otherwise change the blob, simply create a new blob and purge the old one. class ActiveStorage::Blob < ActiveRecord::Base - include Analyzable, Representable + include Analyzable, Identifiable, Representable self.table_name = "active_storage_blobs" has_secure_token :key - store :metadata, accessors: [ :analyzed ], coder: JSON + store :metadata, accessors: [ :analyzed, :identified ], coder: JSON class_attribute :service @@ -136,8 +136,10 @@ class ActiveStorage::Blob < ActiveRecord::Base # Normally, you do not have to call this method directly at all. Use the factory class methods of +build_after_upload+ # and +create_after_upload!+. def upload(io) - self.checksum = compute_checksum_in_chunks(io) - self.byte_size = io.size + self.checksum = compute_checksum_in_chunks(io) + self.content_type = extract_content_type(io) + self.byte_size = io.size + self.identified = true service.upload(key, io, checksum: checksum) end @@ -182,6 +184,10 @@ class ActiveStorage::Blob < ActiveRecord::Base end.base64digest end + def extract_content_type(io) + Marcel::MimeType.for io, name: filename.to_s, declared_type: content_type + end + def forcibly_serve_as_binary? ActiveStorage.content_types_to_serve_as_binary.include?(content_type) end diff --git a/activestorage/app/models/active_storage/blob/identifiable.rb b/activestorage/app/models/active_storage/blob/identifiable.rb new file mode 100644 index 0000000000..40ca84ac70 --- /dev/null +++ b/activestorage/app/models/active_storage/blob/identifiable.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module ActiveStorage::Blob::Identifiable + def identify + ActiveStorage::Identification.new(self).apply + end + + def identified? + identified + end +end diff --git a/activestorage/app/models/active_storage/identification.rb b/activestorage/app/models/active_storage/identification.rb new file mode 100644 index 0000000000..4f295257ae --- /dev/null +++ b/activestorage/app/models/active_storage/identification.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +class ActiveStorage::Identification + attr_reader :blob + + def initialize(blob) + @blob = blob + end + + def apply + blob.update!(content_type: content_type, identified: true) unless blob.identified? + end + + private + def content_type + Marcel::MimeType.for(identifiable_chunk, name: filename, declared_type: declared_content_type) + end + + + def identifiable_chunk + Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |client| + client.get(uri, "Range" => "0-4096").body + end + end + + def uri + @uri ||= URI.parse(blob.service_url) + end + + + def filename + blob.filename.to_s + end + + def declared_content_type + blob.content_type + end +end -- cgit v1.2.3