ActionDispatch::Http::UploadedFile.content_type not being initialized in Rspec test
I looked at the Rails source file for the UploadedFile
class and I found the issue. For the @content_type attribute, for example, while the getter and setters are named as expected (.content_type
), the initialize
method looks for an attribute called type
in the options hash. The same thing happens for @original_filename; initialize looks for filename
instead of original_filename
. This seems to have been the case since the Rails 3 codebase.
So, if I change my code above to the following, everything works as expected:
book.cover_file = ActionDispatch::Http::UploadedFile.new(tempfile: cover_image, filename: File.basename(cover_image), type: "image/jpeg")
Relevant section of rails/actionpack/lib/action_dispatch/http/upload.rb...
class UploadedFile
# The basename of the file in the client.
attr_accessor :original_filename
# A string with the MIME type of the file.
attr_accessor :content_type
# A +Tempfile+ object with the actual uploaded file. Note that some of
# its interface is available directly.
attr_accessor :tempfile
alias :to_io :tempfile
# A string with the headers of the multipart request.
attr_accessor :headers
def initialize(hash) # :nodoc:
@tempfile = hash[:tempfile]
raise(ArgumentError, ':tempfile is required') unless @tempfile
@original_filename = encode_filename(hash[:filename])
@content_type = hash[:type]
@headers = hash[:head]
end
Testing POST ActionDispatch::Http::Parameters::ParseError: 765
Removing the 'Content-Type' => 'application/json'
solved the problem.
Finally remembered that ActionDispatch uses the headers to know how to parse the params. 'Content-Type' => 'application/json'
is a standard piece of boilerplate that's ok to throw around with GET
requests and query params but not with POST
when used this way.
Reading and writing file attributes
It's not documented (and doesn't make much sense), but it looks like the options UploadedFile#initialize
takes are :tempfile
, :filename
, :type
and :head
:
def initialize(hash) # :nodoc:
@tempfile = hash[:tempfile]
raise(ArgumentError, ':tempfile is required') unless @tempfile
@original_filename = encode_filename(hash[:filename])
@content_type = hash[:type]
@headers = hash[:head]
end
Changing your invocation to this ought to work:
ActionDispatch::Http::UploadedFile.new tempfile: 'tempfilefoo',
filename: 'filename_foo.jpg', type: 'content_type_foo', head: 'headers_foo'
Or you can set them after initialization:
file = ActionDispatch::Http::UploadedFile.new tempfile: 'tempfilefoo', filename: 'filename_foo.jpg'
file.content_type = 'content_type_foo'
file.headers = 'headers_foo'
I'm not sure I understand your second question, "And how can I read these attributes from a file instance?"
You can extract the filename (or last component) from any path with File.basename
:
file = File.new('path/to/file.png')
File.basename(file.path) # => "file.png"
If you want to get the Content-Type that corresponds to a file extension, you can use Rails' Mime module:
type = Mime["png"] # => #<Mime::Type:... @synonyms=[], @symbol=:png, @string="text/png">
type.to_s # => "text/png"
You can put this together with File.extname
, which gives you the extension:
ext = File.extname("path/to/file.png") # => ".png"
ext = ext.sub(/^\./, '') # => "png" (drop the leading dot)
Mime[ext].to_s # => "text/png"
You can see a list of all of the MIME types Rails knows about by typing Mime::SET
in the Rails console, or looking at the source, which also shows you how to register other MIME types in case you're expecting other types of files.
ActiveStorage - Could not find or build blob: expected attachable, got #ActionDispatch::Http::UploadedFile
First you need to create blob file in case of active storage.
def attach_doc(doc_param)
doc = ActiveStorage::Blob.create_and_upload!(
io: doc_param,
filename: doc_param.original_filename,
content_type: doc_param.content_type
)
document = context[:current_user].documents.doc.attach(doc)
end
In rails < 7.0 method is ActiveStorage::Blob.create_after_upload
Related Topics
Memory Size of a Hash or Other Object
Running Parallel Selenium Tests with Capybara
How to Get a Substring from Position N to the Last Char in Ruby
Vagrant Install Plugin from Github
Simple Neural Network Can't Learn Xor
Can't Launch Simple Sinatra App Using Rackup and Jruby (No Response from Web Server)
Ruby, Run Linux Commands One by One, by Ssh and Log Everything
How to Pluck Email from Array of Users
Why Does Date Exist in Ruby Before It Is Required
Iterate an Array, N Items at a Time
To Make an API Call with an Oauth Token
Initializing Instance Variables in Mixins
Active Admin: Sorting on Multiple Columns
Accessing One Controller Variable in Another Controller in Rails