Отправить файл ActiveStorage пользователю с помощью настраиваемого действия контроллера

Мне известен метод ActiveStorage url_for(user.avatar). Пример:

<%= link_to "Download this file", url_for(@user.avatar) %>

Это здорово, но похоже, что для этого нет авторизации. Любой, у кого есть эта ссылка, может скачать этот документ.

Раньше, когда я использовал скрепку, у меня была ссылка на настраиваемое действие контроллера. Это настраиваемое действие контроллера выполнило авторизацию, и, если все в порядке, я использовал send_file для отправки файла пользователю. Это было примерно так:

def deliver_the_file
  authorize :my_authorization
  send_file @user.avatar.path
end

Как это сделать с active_storage? Все, что у меня получилось, это реализация url_for, которая вообще не авторизует пользователя.

Я специально смотрю на Загрузить файлы часть руководств Rails по ActiveStorage.

Соответствующий пример кода:

# 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

Это ошибка и говорит:

Не могу прочитать файл

Я также попытался это сделать:

def deliver_it
  # ... assume authorization already happened
  send_file @book.book_cover.download
end

Это вернуло ошибку:

строка содержит нулевой байт

И я тоже попытался это сделать:

def deliver_it
  @book.book_cover.download
end

Это вернуло ошибку:

BooksController # delivery_it отсутствует шаблон для этого запроса.

Я тоже пробовал это:

def deliver_it
  send_file @book.book_cover.blob
end

Это ошибка и говорит:

нет неявного преобразования ActiveStorage :: Blob в String


person Neil    schedule 10.05.2018    source источник


Ответы (3)


Лучше всего использовать ActiveStorage::Blob#service_url для перенаправления на подписанный недолговечный 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
person George Claghorn    schedule 11.05.2018
comment
Я случайно увидел ваше service_url предложение, но оно показало мне ошибку: Отсутствует хост для ссылки! Укажите параметр: host, установите default_url_options [: host] или установите для: only_path значение true - person Neil; 11.05.2018
comment
Извини, @Neil! Я обновил первый пример, чтобы продемонстрировать настройку ActiveStorage::Current.host в соответствии с требованиями дисковой службы. - person George Claghorn; 11.05.2018
comment
Отлично, спасибо! Еще один вопрос: есть ли способ установить, сколько времени будет активна ссылка? Возможно, установить его всего на 30 секунд? - person Neil; 11.05.2018
comment
@ Нил: да, вы можете передать expires_in: 30.seconds service_url. - person George Claghorn; 11.05.2018

Я получил это решение из комментария к этой проблеме с ActiveStorage

def deliver_it
  send_data @book.book_cover.download, filename: @book.book_cover.filename.to_s, content_type: @book.book_cover.content_type
end
person Neil    schedule 10.05.2018

Я использовал redirect_to здесь, и это сработало:

redirect_to rails_blob_path(@book.book_cover, disposition: "attachment")
person kangkyu    schedule 17.09.2018