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

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

今日在 收看 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 方法
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请 注册新账号