如何使用 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 设置文件夹来存储文件