Ruby 当使用 directUpload 时候, 固定删除无关联的 blob 模型

jicheng1014 · 2021年02月01日 · 最后由 aline57963 回复于 2021年02月04日 · 195 次阅读

今日在 收看 driftingruby.com Purge Orphaned Active Storage Records | Drifting Ruby 的时候, 觉得这个思路有点意思, 故总结了下大概的思路

(drifting ruby 是一个不错的 ruby on rails 的视频教程网站, 每月订阅费为 15usd , 各位老哥可以每个月花一顿请客吃饭的钱学习下 ruby 的一些知识。 这是我的订阅推广链接 https://www.driftingruby.com/subscription/new?referral=4ce28682160093d18c0480e9571ef504 ,有兴趣的朋友可以订阅)

为何会存在 无关联的 blob

在 active storage 的上传流程

active storage 的 direct upload 上传的步骤

  1. 拿到图片大小, type, checksum, type, filename
  2. 向服务器 post 请求 /rails/active_storage/direct_uploads 拿到 传输的 blob 信息
  3. 拿到 blob 的返回值里的 direct_upload 来传文件
  4. 将 blob 里的 signed_id 加入到 form 中

当我们默认使用 direct_upload: true 的方式启用 直传的时候, rails 会拦截 默认的 form 提交, 改为: 完成 blob 创建, 文件上传 , 再将 blob id 插入到 form 中, 再次提交, 服务器会再新建一个 attachment 用来标记存储上传完毕的文件, 以及存储 attachment 与 model 的对应关系。 这些动作是一气呵成的, 在外界看来, 就像是自己提交了一个 form, 没什么 ui 级别的区别。

但有两种情况比较尴尬, 文件会上传成功, 但是因为种种原因,没有生成 attachment 记录:

  1. form 校验没过
  2. 因为 ui 等原因, 没有使用默认的 direct_upload: true, 而是通过 js DirectUpload 配合 , 但是用户在 上传完毕文件后,并未提交 form

当这种情况发生时, 我们就会有一个我们永远不会调用的文件存储了, 如果是类似使用了 OSS 啥的文件存储服务, 就会产生 没有价值的 oss 消耗。

那么如何删除这些内容呢?

如何删除 无关联的 Blob

有两种思路, 任意其中一种即可:

  1. cront 跑脚本 删除
  2. 通过 后台任务完成

定时跑脚本

这个很简单, 只需要定时查询出没有关联的 blob, 删除即可。 注意, 这里有个时间的筛选, 是因为有时候存在这样的情况:用户已经上传完毕文件, 但是正在编辑 form 的途中的(特别是我说的方式 2, 使用 js DirectUpload), 所以要避免删除特别新的 blob

比如每天 凌晨

ActiveStorage::Blob.where('id not in (select blob_id from active_storage_attachments)')
                   .where('created_at < ?', 10.minutes.ago)
                   .map(&:purge_later)

使用后台任务

总体思路是 扩展一下 blob, 当 blob 建立后, 加入一个 background job, 用来检测在一段时间后是否有对应的 attachment, 若是没有, 则自行删除

那么 如何改造 blob 让其有这种能力呢?

第一种, 猴子补丁, 直接真刀真枪的复写 ActiveStorage::Blob 文件即可 第二种, 利用 Rails.configuration.to_prepare 在 initializer 中, 委婉的打个补丁

第一种简单暴力, 第二种相对优雅

第二种方式的核心代码是

# config/initializers/blob_include.rb
Rails.configuration.to_prepare do
  ActiveStorage::Blob.include ActiveStorage::BlobExtension
end

之后 利用 ActiveSupport::Concern 的 included 加入一个 after_create 到 blob 对象中.

核心思路已经说清楚了, 再具体的代码就不赘述了, 有兴趣的同学可以订阅 driftingruby.com, 15usd 买不了吃亏买不了上当。

总结

  1. 为了防止 blob 无效占用存储空间, 可以定期或异步检测关联情况。
  2. 优美的给 rails 打补丁可以使用 Rails.configuration.to_prepare 方法
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请 注册新账号