mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Add option to ActiveStorage::Blob to set extract_content_type_from_io
This adds a boolean argument called identify to ActiveStorage::Blob methods #create_after_upload, #build_after_upload and #upload. It allows a user to bypass the automatic content_type inference from the io.
This commit is contained in:
parent
5c7656d9a5
commit
8e98bb7758
5 changed files with 49 additions and 9 deletions
|
@ -1,3 +1,18 @@
|
||||||
|
* Pass in `identify: false` as an argument when providing a `content_type` for
|
||||||
|
`ActiveStorage::Attached::{One,Many}#attach` to bypass automatic content
|
||||||
|
type inference. For example:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
@message.image.attach(
|
||||||
|
io: File.open('/path/to/file'),
|
||||||
|
filename: 'file.pdf',
|
||||||
|
content_type: 'application/pdf',
|
||||||
|
identify: false
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
*Ryan Davidson*
|
||||||
|
|
||||||
* The Google Cloud Storage service properly supports streaming downloads.
|
* The Google Cloud Storage service properly supports streaming downloads.
|
||||||
It now requires version 1.11 or newer of the google-cloud-storage gem.
|
It now requires version 1.11 or newer of the google-cloud-storage gem.
|
||||||
|
|
||||||
|
|
|
@ -44,21 +44,23 @@ class ActiveStorage::Blob < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a new, unsaved blob instance after the +io+ has been uploaded to the service.
|
# Returns a new, unsaved blob instance after the +io+ has been uploaded to the service.
|
||||||
def build_after_upload(io:, filename:, content_type: nil, metadata: nil)
|
# When providing a content type, pass <tt>identify: false</tt> to bypass automatic content type inference.
|
||||||
|
def build_after_upload(io:, filename:, content_type: nil, metadata: nil, identify: true)
|
||||||
new.tap do |blob|
|
new.tap do |blob|
|
||||||
blob.filename = filename
|
blob.filename = filename
|
||||||
blob.content_type = content_type
|
blob.content_type = content_type
|
||||||
blob.metadata = metadata
|
blob.metadata = metadata
|
||||||
|
|
||||||
blob.upload io
|
blob.upload(io, identify: identify)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a saved blob instance after the +io+ has been uploaded to the service. Note, the blob is first built,
|
# Returns a saved blob instance after the +io+ has been uploaded to the service. Note, the blob is first built,
|
||||||
# then the +io+ is uploaded, then the blob is saved. This is done this way to avoid uploading (which may take
|
# then the +io+ is uploaded, then the blob is saved. This is done this way to avoid uploading (which may take
|
||||||
# time), while having an open database transaction.
|
# time), while having an open database transaction.
|
||||||
def create_after_upload!(io:, filename:, content_type: nil, metadata: nil)
|
# When providing a content type, pass <tt>identify: false</tt> to bypass automatic content type inference.
|
||||||
build_after_upload(io: io, filename: filename, content_type: content_type, metadata: metadata).tap(&:save!)
|
def create_after_upload!(io:, filename:, content_type: nil, metadata: nil, identify: true)
|
||||||
|
build_after_upload(io: io, filename: filename, content_type: content_type, metadata: metadata, identify: identify).tap(&:save!)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a saved blob _without_ uploading a file to the service. This blob will point to a key where there is
|
# Returns a saved blob _without_ uploading a file to the service. This blob will point to a key where there is
|
||||||
|
@ -142,13 +144,14 @@ class ActiveStorage::Blob < ActiveRecord::Base
|
||||||
#
|
#
|
||||||
# Prior to uploading, we compute the checksum, which is sent to the service for transit integrity validation. If the
|
# Prior to uploading, we compute the checksum, which is sent to the service for transit integrity validation. If the
|
||||||
# checksum does not match what the service receives, an exception will be raised. We also measure the size of the +io+
|
# checksum does not match what the service receives, an exception will be raised. We also measure the size of the +io+
|
||||||
# and store that in +byte_size+ on the blob record.
|
# and store that in +byte_size+ on the blob record. The content type is automatically extracted from the +io+ unless
|
||||||
|
# you specify a +content_type+ and pass +identify+ as false.
|
||||||
#
|
#
|
||||||
# Normally, you do not have to call this method directly at all. Use the factory class methods of +build_after_upload+
|
# 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!+.
|
# and +create_after_upload!+.
|
||||||
def upload(io)
|
def upload(io, identify: true)
|
||||||
self.checksum = compute_checksum_in_chunks(io)
|
self.checksum = compute_checksum_in_chunks(io)
|
||||||
self.content_type = extract_content_type(io)
|
self.content_type = extract_content_type(io) if content_type.nil? || identify
|
||||||
self.byte_size = io.size
|
self.byte_size = io.size
|
||||||
self.identified = true
|
self.identified = true
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,16 @@ class ActiveStorage::BlobTest < ActiveSupport::TestCase
|
||||||
assert_equal "text/plain", blob.content_type
|
assert_equal "text/plain", blob.content_type
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test "create after upload extracts content_type from io when no content_type given and identify: false" do
|
||||||
|
blob = create_blob content_type: nil, identify: false
|
||||||
|
assert_equal "text/plain", blob.content_type
|
||||||
|
end
|
||||||
|
|
||||||
|
test "create after upload uses content_type when identify: false" do
|
||||||
|
blob = create_blob data: "Article,dates,analysis\n1, 2, 3", filename: "table.csv", content_type: "text/csv", identify: false
|
||||||
|
assert_equal "text/csv", blob.content_type
|
||||||
|
end
|
||||||
|
|
||||||
test "image?" do
|
test "image?" do
|
||||||
blob = create_file_blob filename: "racecar.jpg"
|
blob = create_file_blob filename: "racecar.jpg"
|
||||||
assert_predicate blob, :image?
|
assert_predicate blob, :image?
|
||||||
|
|
|
@ -50,8 +50,8 @@ class ActiveSupport::TestCase
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
def create_blob(data: "Hello world!", filename: "hello.txt", content_type: "text/plain")
|
def create_blob(data: "Hello world!", filename: "hello.txt", content_type: "text/plain", identify: true)
|
||||||
ActiveStorage::Blob.create_after_upload! io: StringIO.new(data), filename: filename, content_type: content_type
|
ActiveStorage::Blob.create_after_upload! io: StringIO.new(data), filename: filename, content_type: content_type, identify: identify
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg", metadata: nil)
|
def create_file_blob(filename: "racecar.jpg", content_type: "image/jpeg", metadata: nil)
|
||||||
|
|
|
@ -319,6 +319,18 @@ type you provide if it can’t do that.
|
||||||
@message.image.attach(io: File.open('/path/to/file'), filename: 'file.pdf', content_type: 'application/pdf')
|
@message.image.attach(io: File.open('/path/to/file'), filename: 'file.pdf', content_type: 'application/pdf')
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can bypass the content type inference from the data by passing in
|
||||||
|
`identify: false` along with the `content_type`.
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
@message.image.attach(
|
||||||
|
io: File.open('/path/to/file'),
|
||||||
|
filename: 'file.pdf',
|
||||||
|
content_type: 'application/pdf'
|
||||||
|
identify: false
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
If you don’t provide a content type and Active Storage can’t determine the
|
If you don’t provide a content type and Active Storage can’t determine the
|
||||||
file’s content type automatically, it defaults to application/octet-stream.
|
file’s content type automatically, it defaults to application/octet-stream.
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue