如何使用 Active Storage 保留存储空间和加载时间?

Posted

技术标签:

【中文标题】如何使用 Active Storage 保留存储空间和加载时间?【英文标题】:How can I preserve storage space and load time with Active Storage? 【发布时间】:2020-10-25 22:21:55 【问题描述】:

我有一个包含图片的用户提交表单。最初我使用的是 Carrierwave,但是图像在保存到 Google 云服务之前首先发送到我的服务器进行处理,如果图像/s 太大,请求超时并且用户只是获得一个服务器错误。

所以我需要一种直接上传到 GCS 的方法。 Active Storage 似乎是完美的解决方案,但我对压缩的难度感到非常困惑。

理想的解决方案是在上传时自动调整图像大小,但似乎没有办法做到这一点。

next-best 解决方案是在上传时使用 @record.images.first.variant(resize_to_limit [xxx,xxx]) #using image_processing gem 之类的东西创建一个调整大小的 variant,但文档似乎暗示一个变体只能是在页面加载时创建,这显然对加载时间极为不利,尤其是在有很多图像的情况下。更多的证据是,当我创建一个变体时,它不在我的 GCS 存储桶中,所以它显然只存在于我的服务器内存中。如果我尝试

@record.images.first.variant(resize_to_limit [xxx,xxx]).service_url

我得到了一个网址,但它是无效的。当我尝试在我的网站上显示图像时,我得到一个失败的图像,当我访问该 url 时,我从 GCS 收到以下错误:

指定的键不存在。 没有这样的对象。

显然我无法创建永久网址。

第三个最好的解决方案是编写一个 Google Cloud 函数来自动调整 Google Cloud 中的图像大小,但是通过 docs 阅读,看来我必须创建一个新的调整大小的使用新 url 文件,我不确定如何用数据库中的新 url 替换原始 url。

总而言之,我想要完成的是允许直接上传到 GCS,但控制文件的大小用户下载它们之前。我对 Active Storage 的问题是(1)我无法控制 GCS 存储桶上文件的大小,从而导致任意存储成本,以及(2)我显然必须在必须下载任意大文件的用户之间进行选择,或者必须在页面加载时处理图像,这两者在服务器成本和加载时间方面都非常昂贵。

以这种方式设置 Active Storage 似乎非常奇怪,我不禁想我错过了一些东西。有谁知道解决这两个问题的方法吗?

【问题讨论】:

在我看来,转储 active_storage 并使用神殿 gem,它将为您处理高度定制的衍生选项。在这里看到它:shrinerb.com/docs/plugins/derivatives @BKSpureon 试了一下,但不幸的是,Google Cloud Services 文档似乎不存在。 神殿与云无关。为此,您必须依赖第三方 gem:github.com/renchap/shrine-google_cloud_storage - 它应该开箱即用,只需进行(少量)修改以使 API 符合要求。我 99% 确定作者也在使用衍生工具。我无法想象它需要超过 20 分钟才能让它工作。 @BKSpureon 你知道 Uppy 的 GCS 解决方案吗?那似乎也只有 S3 文档。 uppy 应该与 google 的 api 一起使用,只需对神殿示例代码进行微小的更改——我已经成功地使用 uppy 和神殿将文件上传到谷歌的云存储,但在我的特定用例中,我只是简单地切换到 AWS,没有太多麻烦(鉴于我的特定用例)。话虽如此:如果 AWS API 发生变化,它可能会成为一个绊脚石——我不确定 uppy 开发团队是否将支持谷歌作为高优先级,我也不知道社区是否已经通过拉取请求来支持GCS。 【参考方案1】:

这是我为解决此问题所做的:

1- 我将用户直接添加的附件上传到我的服务提供商(我使用 S3)。

2- 我添加了一个 after_commit 作业,该作业调用 Sidekiq 工作人员来生成拇指

3- 我的 sidekiq 工作人员 (AttachmentWorker) 调用我模型的 generate_thumbs 方法

4- generate_thumbs 将循环遍历我想为此文件生成的不同大小

现在,棘手的部分来了:

def generate_thumbs
  [
     resize: '300x300^', extent: '300x300', gravity: :center ,
     resize: '600>' 
  ].each do |size|
    self.file_url(size, true)
  end
end

def file_url(size, process = false)
  value = self.file # where file is my has_one_attached
  
  if size.nil?
    url = value
  else
    url = value.variant(size)

    if process
      url = url.processed
    end
  end

  return url.service_url
end

file_url方法中,如果我们通过process = true,我们只会调用.processed。我已经对这种方法进行了很多试验,以便从中获得最佳的性能结果。

.processed会检查你的bucket文件是否存在,如果不存在,它会生成你的新文件并上传。

另外,还有一个我之前问过的关于 ActiveStorage 的问题也可以帮助您:ActiveStorage & S3: Make files public

【讨论】:

【参考方案2】:

我绝对不知道 Active Storage。但是,对于您的用例来说,一个好的模式是在图像进入时调整其大小。为此

让用户将图片存储在 Bucket1 中 在 Bucket1 中创建文件时,event is triggered. Plug a function on this event Cloud Functions 调整图像大小并将其存储到 Bucket2 中 您可以在 Cloud Function 结束时删除 Bucket1 中的图像,或者将其保留几天或将其移至更便宜的存储空间(以保留原始图像以防出现问题)。对于这最后 2 个操作,您可以使用 Life Cycle 删除或更改文件的存储类别。

注意:您可以使用同一个 Bucket(而不是 Bucket1 和 Bucket2),但是每次在 Bucket 中创建文件时都会发送一个调整图像大小的事件。您可以使用 PubSub 作为中间件并在其上添加过滤器以触发您的功能,只有在正确的文件夹中创建文件。我wrote an article on this

【讨论】:

这是个好主意,但是通过阅读文档,我似乎无法对调整大小的图像使用相同的 URL,这是有问题的,因为我不知道如何关联带有我的数据库记录的调整大小图像的 URL。

以上是关于如何使用 Active Storage 保留存储空间和加载时间?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Rails 和 Active Storage 实现 AWS S3 分段上传?

如何在使用 gsutil 保留 ACL 的同时将文件从 Google Cloud Storage 存储桶 1 复制到存储桶 2

Rails Active Storage 设置文件夹来存储文件

Active Storage,指定一个谷歌存储桶目录?

如何从 Rails Active Storage 中的 url 附加图像

Ruby on Rails - Active Storage - 如何只接受 pdf 和 doc?