Rails + S3 上的 ActiveStorage:在下载时设置文件名?
Rails + ActiveStorage on S3: Set filename on download?
有没有办法 change/set 下载文件名?
示例:Jon Smith 上传了他的头像,文件名为 4321431-small.jpg
。下载时,我想将文件重命名为 jon_smith__headshot.jpg
.
查看:
<%= url_for user.headshot_file %>
这 url_for
从 Amazon S3 下载文件,但使用原始文件名。
我有哪些选择?
我还没有玩过 ActiveStorage,所以这是盲目尝试。
查看 ActiveStorage source for the S3 service, it looks like you can specify the filename and disposition for the upload. From the guides,您似乎可以使用 rails_blob_path
访问上传的原始 URL 并传递这些参数。因此你可以试试:
rails_blob_url(user.headshot_file, filename: "jon_smith__headshot.jpg")
内置控制器为 blob 提供存储的文件名。您可以实现一个自定义控制器,为它们提供不同的文件名:
class HeadshotsController < ApplicationController
before_action :set_user
def show
redirect_to @user.headshot.service_url(filename: filename)
end
private
def set_user
@user = User.find(params[:user_id])
end
def filename
ActiveStorage::Filename.new("#{user.name.parameterize(separator: "_")}__headshot#{user.headshot.filename.extension_with_delimiter}")
end
end
从 5.2.0 RC2 开始,您将不再需要传递 ActiveStorage::Filename
;您可以改为传递 String
文件名。
我知道这个问题已经得到解答,但我想添加第二种方法。您可以在保存用户对象时更新文件名。使用 OP 的 user
模型和 headshot_file
字段示例,这就是解决此问题的方法:
# app/models/user.rb
after_save :set_filename
def set_filename
file.blob.update(filename: "ANYTHING_YOU_WANT.#{file.filename.extension}") if file.attached?
end
@GuilPejon 的方法会奏效。直接调用 service_url
的问题是:
- 它是短暂的(rails 团队不推荐)
- 如果服务处于
disk
开发模式,则无法使用。
它对磁盘服务不起作用的原因是磁盘服务需要 ActiveStorage::Current.host
才能生成 URL。 ActiveStorage::Current.host
在 app/controllers/active_storage/base_controller.rb
中设置,因此在调用 service_url
时它将丢失。
ActiveStorage 目前提供了另一种访问附件 URL 的方法:
- 使用
rails_blob_(url|path)
(推荐方式)
但是如果你使用这个,你只能提供content-disposition
而不能提供文件名。
如果您在 `ActiveStorage 存储库中看到 config/routes.rb
,您将找到以下代码。
get "/rails/active_storage/blobs/:signed_id/*filename" => "active_storage/blobs#show", as: :rails_service_blob
direct :rails_blob do |blob, options|
route_for(:rails_service_blob, blob.signed_id, blob.filename, options)
end
当您查看 blobs_controller
时,您会发现以下代码:
def show
expires_in ActiveStorage::Blob.service.url_expires_in
redirect_to @blob.service_url(disposition: params[:disposition])
end
所以很明显,在rails_blob_(url|path)
中你只能通过disposition
,仅此而已。
有没有办法 change/set 下载文件名?
示例:Jon Smith 上传了他的头像,文件名为 4321431-small.jpg
。下载时,我想将文件重命名为 jon_smith__headshot.jpg
.
查看:
<%= url_for user.headshot_file %>
这 url_for
从 Amazon S3 下载文件,但使用原始文件名。
我有哪些选择?
我还没有玩过 ActiveStorage,所以这是盲目尝试。
查看 ActiveStorage source for the S3 service, it looks like you can specify the filename and disposition for the upload. From the guides,您似乎可以使用 rails_blob_path
访问上传的原始 URL 并传递这些参数。因此你可以试试:
rails_blob_url(user.headshot_file, filename: "jon_smith__headshot.jpg")
内置控制器为 blob 提供存储的文件名。您可以实现一个自定义控制器,为它们提供不同的文件名:
class HeadshotsController < ApplicationController
before_action :set_user
def show
redirect_to @user.headshot.service_url(filename: filename)
end
private
def set_user
@user = User.find(params[:user_id])
end
def filename
ActiveStorage::Filename.new("#{user.name.parameterize(separator: "_")}__headshot#{user.headshot.filename.extension_with_delimiter}")
end
end
从 5.2.0 RC2 开始,您将不再需要传递 ActiveStorage::Filename
;您可以改为传递 String
文件名。
我知道这个问题已经得到解答,但我想添加第二种方法。您可以在保存用户对象时更新文件名。使用 OP 的 user
模型和 headshot_file
字段示例,这就是解决此问题的方法:
# app/models/user.rb
after_save :set_filename
def set_filename
file.blob.update(filename: "ANYTHING_YOU_WANT.#{file.filename.extension}") if file.attached?
end
@GuilPejon 的方法会奏效。直接调用 service_url
的问题是:
- 它是短暂的(rails 团队不推荐)
- 如果服务处于
disk
开发模式,则无法使用。
它对磁盘服务不起作用的原因是磁盘服务需要 ActiveStorage::Current.host
才能生成 URL。 ActiveStorage::Current.host
在 app/controllers/active_storage/base_controller.rb
中设置,因此在调用 service_url
时它将丢失。
ActiveStorage 目前提供了另一种访问附件 URL 的方法:
- 使用
rails_blob_(url|path)
(推荐方式)
但是如果你使用这个,你只能提供content-disposition
而不能提供文件名。
如果您在 `ActiveStorage 存储库中看到 config/routes.rb
,您将找到以下代码。
get "/rails/active_storage/blobs/:signed_id/*filename" => "active_storage/blobs#show", as: :rails_service_blob
direct :rails_blob do |blob, options|
route_for(:rails_service_blob, blob.signed_id, blob.filename, options)
end
当您查看 blobs_controller
时,您会发现以下代码:
def show
expires_in ActiveStorage::Blob.service.url_expires_in
redirect_to @blob.service_url(disposition: params[:disposition])
end
所以很明显,在rails_blob_(url|path)
中你只能通过disposition
,仅此而已。