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?
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:
Shrine.mime_type
already. Use the value you get from this and store it in a separate database column mime_type
on your id_docs
model and then use it when you're reading from the database. Note that this probably implicitly means that the value in this column and the value you get from the file's metadata's mime type are out of sync.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