ruby-on-railsshrine

setting mime type for image files before they are encrypted with Shrine and Rails


My app encrypts and uploads certain files, and then makes them visible to admins. To achieve the latter functionality, my encryption gem's documentation suggests a controller action that looks like this:

def show
 user = User.find(params[:id])
 lockbox = Lockbox.new(key: Lockbox.attribute_key(table: "id_docs", attribute: "image"))
 send_data lockbox.decrypt(user.id_docs.image.read), type: user.id_docs.image.mime_type, disposition: 'inline'
end

I want the file to stream, but the browser does not know how to interpret it, and downloads instead. This happens the files are encrypted before upload, and Shrine sets the mime type of these files to application/octet-stream.

My create action looks like this:

def create
 image = params.require(:id_doc).fetch(:image)
 respond_to do |format|
  if Shrine.mime_type(image) == 'image/jpeg'
   lockbox = Lockbox.new(key: Lockbox.attribute_key(table: "id_docs", attribute: "image"))
   encrypted_image = lockbox.encrypt_io(image)
   @id_doc = IdDoc.create(user_id: current_user.id, image: encrypted_image)
   @id_doc.save
   format.html { redirect_to root_path }
  else
   format.html { redirect_to current_path }
  end
 end
end

If I do not encrypt the files, the mime type is saved as image/png or image/jpeg, which is what I want.

IdDoc.rb has a virtual attribute called :image which maps to an image_data field in the database:

class IdDoc < ApplicationRecord
 belongs_to :user
 validates_presence_of :image
 include IdDocUploader::Attachment(:image)
end

schema.rb

create_table "id_docs", force: :cascade do |t|
 t.bigint "user_id"
 t.text "image_data"
end

The data saved to image_data is saved in json format: {\"id\":\"iddoc/1/image/2de32e77f306f0e95aed24623e930683.png\",\"storage\":\"store\",\"metadata\":{\"filename\":\"Screen Shot 2020-10-31 at 7.24.08 AM.png\",\"size\":47364,\"mime_type\":\"application/octet-stream\"}}

How would I change the value of mime_type before the file creates? Is there any way to do this with Shrine, or should I go super hacky and parse that json directly?


Solution

  • Simply said: I think you're not quite doing this in the way Shrine intends and there's multiple ways to remedy this. I'll rank them from (in my opinion, based on complexity/appropriateness) best to worst:


    Using the attacher directly:

    mime_type = Shrine.mime_type(image)
    
    # ...
    
    @id_doc = IdDoc.create(user_id: current_user.id) do |id_doc|
      id_doc.image_attacher.model_assign(encrypted_image, metadata: { mime_type: mime_type })
    end