ruby-on-rails - 使用不同的文件名附加 ActiveStorage blob

标签 ruby-on-rails file-upload rails-activestorage

我正在寻找附加/复制具有不同名称的 ActiveStorage blob 的最优雅的方式。

我目前最好的解决方案是使用 IO 接口(interface)并下载和重新上传文件,这似乎效率很低。

url = Rails.application.routes.url_helpers.rails_blob_url( contract.document )
attachements.push([
  io: open( path_url ),
  filename: "New contract filename"
])

我知道你可以做到这一点,这只涉及到数据库:

contract.attach( contract.document.blob )

但我需要更改文件名。

ActiveStorage 文档在这个主题上似乎参差不齐,我无法真正找到我要找的东西。

最佳答案

Active Storage 的限制意味着您肯定需要复制 blob。在 Active Storage 中,blob 键是唯一索引的,因此它不能被不同的文件名引用两次。

如果您的存储服务允许您在原地有效地复制 blob,那么 Rails 端的启用方法是 create_before_direct_upload! .尽管名称有些误导,但专门记录了此方法以提供新的(但未附加的)blob 记录,期望内容本身将上传到应用程序之外。然后在后续操作中附加 blob 记录。

这就是 Direct Upload 的工作原理,我们可以连接到相同的两阶段机制;在这种特殊情况下,“上传”将改为特定于服务的复制。

原地复制的方式因存储服务而异,例如:

因此,一段说明性的演示代码可能是:

def blob_clone(current_blob, **options)
  blob_parameters = { 
    filename: current_blob.filename,
    byte_size: current_blob.byte_size,
    checksum: current_blob.checksum,
    content_type: current_blob.content_type,
    metadata: current_blob.metadata,
    **options
  }

  service = current_blob.service
  new_blob = ActiveStorage::Blob.create_before_direct_upload!(**blob_parameters)

  case service.class.name
  when 'ActiveStorage::Service::S3Service'
    bucket = service.bucket
    current_object = bucket.object(current_blob.key)
    current_object.copy_to(bucket.object(new_blob.key))
  when 'ActiveStorage::Service::DiskService'
    current_path = service.path_for(current_blob.key)
    new_path = service.path_for(new_blob.key)
    FileUtils.mkdir_p(File.dirname(new_path))
    FileUtils.ln(current_path, new_path)
  else
    raise ArgumentError, "unknown blob storage service #{service.class.name}"
  end

  new_blob
end

这样使用:

current_blob = mymodel.contract.blob
new_blob = blob_clone(current_blob, filename: 'newfilename.pdf')
new_model.contract.attach(new_blob)

尽管我建议根据您的应用程序结构和偏好重构此示例。

代码注释

在上面的插图中,case service.class.name 相当不可爱,只是为了保持演示代码的独立性而编写的。问题在于 Rails 甚至不为未使用的后端存储服务加载类常量。如果您没有跨环境使用不同的存储服务,那么 case 语句可能是不必要的,如果这完全实现为一个 gem,或者重构为一个初始化程序,我可能会把它巧妙地修改为 monkey-patching/refining服务类本身。

关于ruby-on-rails - 使用不同的文件名附加 ActiveStorage blob,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66103388/

相关文章:

ruby-on-rails - 激活 Ruby RAKE 10.0.1,需要 10.0.0

Angular HttpClient HttpErrorResponse JSON 中的意外 token 位于位置 0

ruby-on-rails - 如何获取我的 Rails Controller 中存储在事件存储中的附件的 URL

ruby-on-rails - ActiveStorage + AWS 文件加密

ruby-on-rails - Active Storage 的 'content_type' 有什么不同?

ruby-on-rails - 通过父属性进行 ActiveRecord 验证

ruby-on-rails - 使用 Rack ruby​​ 服务器接收 POST

php - 使用 file_get_contents vs curl 获取文件大小

http - 获取使用 net/http request.ParseMultipartForm 处理的上传文件的 Content-Type header

ruby-on-rails - RUBY 中的日期格式