通过自定义控制器操作将 ActiveStorage 文件发送给用户
Send ActiveStorage File to User Via Custom Controller Action
我知道 ActiveStorage url_for(user.avatar)
方法。示例:
<%= link_to "Download this file", url_for(@user.avatar) %>
这很好,但它似乎没有建立围绕它的授权。拥有此 link 的任何人都可以下载此文档。
过去当我使用 paperclip 时,我有一个 link 用于自定义控制器操作。该自定义控制器操作进行了授权,如果一切正常,那么我使用 send_file
将文件发送给用户。它是这样的:
def deliver_the_file
authorize :my_authorization
send_file @user.avatar.path
end
如何使用 active_storage 执行此操作?我所要做的只是 url_for
实现,它根本不授权用户。
我正在专门查看 Rails ActiveStorage 指南的 Download Files 部分。
相关示例代码:
# model
class Book < ApplicationRecord
has_one_attached :book_cover
end
class BooksController < ApplicationController
...
# custom action to authorize and then deliver the active_storage file
def deliver_it
# ... assume authorization already happened
send_file rails_blob_path(@book.book_cover, disposition: "attachment")
end
end
这个错误说:
Cannot read file
我也试过这个:
def deliver_it
# ... assume authorization already happened
send_file @book.book_cover.download
end
这返回了错误:
string contains null byte
我也试过这个:
def deliver_it
@book.book_cover.download
end
这返回了错误:
BooksController#deliver_it is missing a template for this request
我也试过这个:
def deliver_it
send_file @book.book_cover.blob
end
这个错误说:
no implicit conversion of ActiveStorage::Blob into String
的评论中得出这个解决方案
def deliver_it
send_data @book.book_cover.download, filename: @book.book_cover.filename.to_s, content_type: @book.book_cover.content_type
end
您最好的选择是使用 ActiveStorage::Blob#service_url
重定向到 blob 的已签名的、短暂的 URL:
class Books::CoversController < ApplicationController
before_action :set_active_storage_host, :set_book
def show
redirect_to @book.cover.service_url
end
private
def set_active_storage_host
ActiveStorage::Current.host = request.base_url
end
def set_book
@book = Current.person.books.find(params[:book_id])
end
end
或者,您自己提供文件。您可以使用 ActionController::Live
将文件流式传输到客户端,而不是一次将整个文件读入内存。
class Books::CoversController < ApplicationController
include ActionController::Live
before_action :set_book
def show
response.headers["Content-Type"] = @book.cover.content_type
response.headers["Content-Disposition"] = "attachment; #{@book.cover.filename.parameters}"
@book.cover.download do |chunk|
response.stream.write(chunk)
end
ensure
response.stream.close
end
# ...
end
我在这里使用了 redirect_to
并且有效:
redirect_to rails_blob_path(@book.book_cover, disposition: "attachment")
我知道 ActiveStorage url_for(user.avatar)
方法。示例:
<%= link_to "Download this file", url_for(@user.avatar) %>
这很好,但它似乎没有建立围绕它的授权。拥有此 link 的任何人都可以下载此文档。
过去当我使用 paperclip 时,我有一个 link 用于自定义控制器操作。该自定义控制器操作进行了授权,如果一切正常,那么我使用 send_file
将文件发送给用户。它是这样的:
def deliver_the_file
authorize :my_authorization
send_file @user.avatar.path
end
如何使用 active_storage 执行此操作?我所要做的只是 url_for
实现,它根本不授权用户。
我正在专门查看 Rails ActiveStorage 指南的 Download Files 部分。
相关示例代码:
# model
class Book < ApplicationRecord
has_one_attached :book_cover
end
class BooksController < ApplicationController
...
# custom action to authorize and then deliver the active_storage file
def deliver_it
# ... assume authorization already happened
send_file rails_blob_path(@book.book_cover, disposition: "attachment")
end
end
这个错误说:
Cannot read file
我也试过这个:
def deliver_it
# ... assume authorization already happened
send_file @book.book_cover.download
end
这返回了错误:
string contains null byte
我也试过这个:
def deliver_it
@book.book_cover.download
end
这返回了错误:
BooksController#deliver_it is missing a template for this request
我也试过这个:
def deliver_it
send_file @book.book_cover.blob
end
这个错误说:
no implicit conversion of ActiveStorage::Blob into String
def deliver_it
send_data @book.book_cover.download, filename: @book.book_cover.filename.to_s, content_type: @book.book_cover.content_type
end
您最好的选择是使用 ActiveStorage::Blob#service_url
重定向到 blob 的已签名的、短暂的 URL:
class Books::CoversController < ApplicationController
before_action :set_active_storage_host, :set_book
def show
redirect_to @book.cover.service_url
end
private
def set_active_storage_host
ActiveStorage::Current.host = request.base_url
end
def set_book
@book = Current.person.books.find(params[:book_id])
end
end
或者,您自己提供文件。您可以使用 ActionController::Live
将文件流式传输到客户端,而不是一次将整个文件读入内存。
class Books::CoversController < ApplicationController
include ActionController::Live
before_action :set_book
def show
response.headers["Content-Type"] = @book.cover.content_type
response.headers["Content-Disposition"] = "attachment; #{@book.cover.filename.parameters}"
@book.cover.download do |chunk|
response.stream.write(chunk)
end
ensure
response.stream.close
end
# ...
end
我在这里使用了 redirect_to
并且有效:
redirect_to rails_blob_path(@book.book_cover, disposition: "attachment")