如何在 Rails 5.2 中复制存储在 ActiveStorage 中的文件
Posted
技术标签:
【中文标题】如何在 Rails 5.2 中复制存储在 ActiveStorage 中的文件【英文标题】:How do I duplicate a file stored in ActiveStorage in Rails 5.2 【发布时间】:2018-09-12 20:46:51 【问题描述】:我有一个使用 ActiveStorage 的模型:
class Package < ApplicationRecord
has_one_attached :poster_image
end
如何创建包含初始 poster_image 文件副本的 Package 对象的副本。大致如下:
original = Package.first
copy = original.dup
copy.poster_image.attach = original.poster_image.copy_of_file
【问题讨论】:
【参考方案1】:对本杰明的回答略有不同确实对我有用。
copy.poster_image.attach(
io: StringIO.new(original.poster_image.blob.download),
filename: original.poster_image.blob.filename,
content_type: original.poster_image.blob.content_type
)
【讨论】:
谢谢!这个带有文件名和 content_type 的扩展版本对我有用【参考方案2】:它对我有用:
copy.poster_image.attach(original.poster_image.blob)
【讨论】:
为什么这不是最佳答案? :)【参考方案3】:通过查看 Rails 的测试找到答案,特别是 in the blob model test
所以对于这种情况
class Package < ApplicationRecord
has_one_attached :poster_image
end
您可以这样复制附件
original = Package.first
copy = original.dup
copy.poster_image.attach \
:io => StringIO.new(original.poster_image.download),
:filename => original.poster_image.filename,
:content_type => original.poster_image.content_type
同样的方法适用于has_many_attachments
class Post < ApplicationRecord
has_many_attached :images
end
original = Post.first
copy = original.dup
original.images.each do |image|
copy.images.attach \
:io => StringIO.new(image.download),
:filename => image.filename,
:content_type => image.content_type
end
【讨论】:
【参考方案4】:在 rails 5 Jethro's answer 运行良好。对于 Rails 6,我必须修改为:
image_io = source_record.image.download
ct = source_record.image.content_type
fn = source_record.image.filename.to_s
ts = Time.now.to_i.to_s
new_blob = ActiveStorage::Blob.create_and_upload!(
io: StringIO.new(image_io),
filename: ts + '_' + fn,
content_type: ct,
)
new_record.image.attach(new_blob)
来源:
Rails 6 Active Storage tests Blob tests【讨论】:
按预期工作。谢谢!【参考方案5】:更新您的模型:
class Package < ApplicationRecord
has_one_attached :poster_image
end
将源包的海报图像 blob 附加到目标包:
source_package.dup.tap do |destination_package|
destination_package.poster_image.attach(source_package.poster_image.blob)
end
【讨论】:
我发现我还必须将依赖选项设置为 false 以避免在删除原始包时删除 blob:has_one_attached :poster_image, dependent: false
是的!抱歉忘记了。
除非我遗漏了什么,否则这不会创建文件的副本。它只是使两个记录都指向同一个文件。即使与文件关联的所有记录都被销毁,它也会将文件留在存储系统上。
其实,你确定这是最新的吗?我刚试过。似乎ActiveStorage::PurgeJob
执行SELECT 1 AS one FROM "active_storage_attachments" WHERE "active_storage_attachments"."blob_id" = 55 LIMIT 1
并且如果有其他附件引用它,则不会删除该blob。所以dependent: false
不是必须的吗?
我指的是this before destroy。【参考方案6】:
如果您想要文件的完整副本,以便原始记录和克隆记录都有自己的附件副本,请执行以下操作:
在 Rails 5.2 中,抓取 this code 并将其放入 config/initializers/active_storage.rb
,然后使用此代码进行复制:
ActiveStorage::Downloader.new(original.poster_image).download_blob_to_tempfile do |tempfile|
copy.poster_image.attach(
io: tempfile,
filename: original.poster_image.blob.filename,
content_type: original.poster_image.blob.content_type
)
end
在 Rails 5.2 之后(只要发布包含this commit),您就可以这样做:
original.poster_image.blob.open do |tempfile|
copy.poster_image.attach(
io: tempfile,
filename: original.poster_image.blob.filename,
content_type: original.poster_image.blob.content_type
)
end
感谢 George,您的原始回答和 Rails 贡献。 :)
【讨论】:
很遗憾,除了master之外,第二部分仍然没有在Rails版本中发布。以上是关于如何在 Rails 5.2 中复制存储在 ActiveStorage 中的文件的主要内容,如果未能解决你的问题,请参考以下文章
在 Postgresql 中使用 UUID 的 Rails 5.2 活动存储
如何在 Amazon S3 中自定义 Rails 5.2 ActiveStorage 附件的路径?
在 Ruby on Rails 项目中将“关注点”存储在哪里? (轨道 5.2+)