在使用 Shrine 和 Rails 加密之前为图像文件设置 mime 类型

Posted

技术标签:

【中文标题】在使用 Shrine 和 Rails 加密之前为图像文件设置 mime 类型【英文标题】:setting mime type for image files before they are encrypted with Shrine and Rails 【发布时间】:2021-03-16 01:28:30 【问题描述】:

我的应用会加密并上传某些文件,然后让管理员可以看到它们。为了实现后一种功能,my encryption gem's documentation 建议使用如下所示的控制器操作:

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

我希望文件流式传输,但浏览器不知道如何解释它,而是下载。发生这种情况,文件在上传前已加密,Shrine 将这些文件的 mime 类型设置为 application/octet-stream

我的创建操作如下所示:

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

如果我加密文件,mime 类型将保存为image/pngimage/jpeg,这正是我想要的。

IdDoc.rb 有一个名为 :image 的虚拟属性,它映射到数据库中的 image_data 字段:

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

保存到image_data的数据保存为json格式:\"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\"

如何在文件创建之前更改mime_type 的值?有什么办法可以用 Shrine 做到这一点,还是我应该去超级 hacky 并直接解析那个 json?

【问题讨论】:

在将文件返回到浏览器时,您是否查看过我的类型?您可能会发现这是导致下载的原因。 @jad 我如何检查这个?我找到了这个alexkras.com/…,但我很困惑,因为我在执行此操作时不进行 API 调用 type: user.id_docs.image.mime_type ...你检查过类型是什么吗? "Application/octet-stream" 是二进制格式。如果您需要图像,则需要返回与数据匹配的图像 mime-type ... 例如“image/jpeg” 您可以尝试使用类似:flylib.com/books/en/2.44.1/… 并强制文件格式为 jpeg 【参考方案1】:

简单地说:我认为你没有完全按照 Shrine 的意图这样做,有多种方法可以解决这个问题。我会从最好到最差(在我看来,基于复杂性/适当性)对它们进行排名:

加密可以/应该被视为 Shrine 处理/衍生概念的一部分。因此,您希望在那里执行处理并相应地设置元数据。 Shrine 作者本人在此处的线程中概述了处理原始文件的解决方案:https://discourse.shrinerb.com/t/keep-original-file-after-processing/50。 您可以手动覆盖元数据:https://shrinerb.com/docs/metadata#controlling-extraction。这可能意味着不使用基于属性的赋值,而是直接调用附加程序(见下文)。 您可以定义自己的 MIME 类型确定逻辑,如下所述:https://shrinerb.com/docs/plugins/determine_mime_type。 您已经在使用Shrine.mime_type。使用您从中获得的值并将其存储在 id_docs 模型上的单独数据库列 mime_type 中,然后在您从数据库中读取时使用它。请注意,这可能暗示此列中的值与您从文件元数据的 mime 类型中获得的值不同步。

直接使用attacher:

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

【讨论】:

以上是关于在使用 Shrine 和 Rails 加密之前为图像文件设置 mime 类型的主要内容,如果未能解决你的问题,请参考以下文章

Rails 5 + Shrine + 多态模型 + 漂亮的位置

Rails 6 - Shrine - ImageProcessing - 获取原始上传文件

Shrine Gem Ruby on Rails 无服务器图像处理程序,后台作业

从 ActiveStorage 迁移到 Shrine 的 Rake 任务

如何使用 Shrine RemoteUrl 插件绕过 SSL 验证

使用 Shrine 和 MiniMagick 将 GIF 转换为 JPEG