views:

509

answers:

1

I am trying to upload a file from a flash widget to my rails application, which uses attachment_fu to handle uplaoded images. I am using flash to upload since it makes it easy to select and upload multiple files. However, I am getting this error when the rails controller tries to call save! on the newly created ActiveRecord object:

ActiveRecord::RecordInvalid (Validation failed: Content type is not included in the list):
    /usr/local/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/validations.rb:946:in `save_without_transactions!'
    /usr/local/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/transactions.rb:112:in `save!'
    /usr/local/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/connection_adapters/abstract/database_statements.rb:66:in `transaction'
    /usr/local/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/transactions.rb:80:in `transaction'
    /usr/local/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/transactions.rb:100:in `transaction'
    /usr/local/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/transactions.rb:112:in `save!'
    /usr/local/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/transactions.rb:120:in `rollback_active_record_state!'
    /usr/local/lib/ruby/gems/1.8/gems/activerecord-2.0.2/lib/active_record/transactions.rb:112:in `save!'
    /app/controllers/photos_controller.rb:13:in `create'
    /usr/local/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/base.rb:1158:in `send'
    /usr/local/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/base.rb:1158:in `perform_action_without_filters'
    /usr/local/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/filters.rb:697:in `call_filters'
    /usr/local/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/filters.rb:689:in `perform_action_without_benchmark'
    /usr/local/lib/ruby/gems/1.8/gems/actionpack-2.0.2/lib/action_controller/benchmarking.rb:68:in `perform_action_without_rescue'

So, it seems that the content type is not being sent correctly. As a matter of fact, Flash is sending a content type of application/octet-stream, where I would expect it to be image/png (for this particular test case).

My ActionScript 3.0 code that performs the upload looks like this:

var request:URLRequest = new URLRequest(paramObj.serverUrl + "/albums/" + paramObj.albumId + "/photos");
var variables:URLVariables = new URLVariables();
variables["photo[title]"] = file.name;
variables["authenticity_token"] = paramObj.authenticity_token;
variables["commit"] = "Upload Photo";
request.data = variables;
request.method = URLRequestMethod.POST;
file.upload(request, 'photo[uploaded_data]');

The form parameters are all present in the Flash upload that you expect from the regular browser upload. After running a packet sniffer, the only real difference I can see is that the content type is different.

The model uses attachment_fu, andlooks like this:

class Photo < ActiveRecord::Base
  belongs_to :album

  has_attachment :content_type => :image, 
                 :storage => :file_system, 
                 :max_size => 10.megabytes, 
                 :thumbnails => { 
                   :thumb  => '100x100>',  
                   :large  => '800x600>', 
                 } 

  validates_as_attachment
end

So, how can I fix the content type that flash sends? And, why does attachment_fu trust the content type sent by the browser, rather than determining it on its own, using magic numbers or something?

I have noticed that if I remove the :content_type => :image or the validates_as_attachment, or if I change the controller to call save(false), the object gets created, but attachment_fu doesn't do its job of resizing the image.

+4  A: 

Unfortunately, Flash uploads are always sent with content type "application/octet-stream". Probably the easiest workaround would be to use mimetype_fu to guess the mimetype in a before_validation callback. Paperclip has this functionality built in.

Another option is to overwrite is_image? to use your image processor to actually determine whether the file is an image (in rmagick for instance, you can open it and see if it has any layers). This is definitely a more resource-intensive solution but it ensures that you accept all the files you can process and no others.

Sam
I really wish I'd known about mimetype_fu a couple of months ago! Would have saved us a lot of time. Upvoted.
Sam Stokes
mimetype_fu looks really interesting. Can it work before the uploaded file exists on the filesystem?
pkaeding