diff --git a/.rubocop.yml b/.rubocop.yml index 174891ed50..3efad3cc53 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -89,9 +89,6 @@ Layout/EmptyLinesAroundModuleBody: Style/HashSyntax: Enabled: true -Layout/FirstArgumentIndentation: - Enabled: true - # Method definitions after `private` or `protected` isolated calls need one # extra level of indentation. Layout/IndentationConsistency: diff --git a/activestorage/CHANGELOG.md b/activestorage/CHANGELOG.md index 8eb24c8791..b3a87fc8de 100644 --- a/activestorage/CHANGELOG.md +++ b/activestorage/CHANGELOG.md @@ -1,3 +1,20 @@ +* Add `ActiveStorage::Streaming` module that can be included in a controller to get access to `#send_blob_stream`, + which wraps the new `ActionController::Base#send_stream` method to stream a blob from cloud storage: + + ```ruby + class MyPublicBlobsController < ApplicationController + include ActiveStorage::SetBlob, ActiveStorage::Streaming + + def show + http_cache_forever(public: true) do + send_blob_stream @blob, disposition: params[:disposition] + end + end + end + ``` + + *DHH* + * Add ability to use pre-defined variants. ```ruby diff --git a/activestorage/app/controllers/active_storage/base_controller.rb b/activestorage/app/controllers/active_storage/base_controller.rb index 63de528409..7f257de4cd 100644 --- a/activestorage/app/controllers/active_storage/base_controller.rb +++ b/activestorage/app/controllers/active_storage/base_controller.rb @@ -2,18 +2,9 @@ # The base class for all Active Storage controllers. class ActiveStorage::BaseController < ActionController::Base - include ActiveStorage::SetCurrent + include ActiveStorage::SetCurrent, ActiveStorage::Streaming protect_from_forgery with: :exception self.etag_with_template_digest = false - - private - def stream(blob) - blob.download do |chunk| - response.stream.write chunk - end - ensure - response.stream.close - end end diff --git a/activestorage/app/controllers/active_storage/blobs/proxy_controller.rb b/activestorage/app/controllers/active_storage/blobs/proxy_controller.rb index 9b4993f240..d5c9707374 100644 --- a/activestorage/app/controllers/active_storage/blobs/proxy_controller.rb +++ b/activestorage/app/controllers/active_storage/blobs/proxy_controller.rb @@ -3,12 +3,10 @@ # Proxy files through application. This avoids having a redirect and makes files easier to cache. class ActiveStorage::Blobs::ProxyController < ActiveStorage::BaseController include ActiveStorage::SetBlob - include ActiveStorage::SetHeaders def show http_cache_forever public: true do - set_content_headers_from @blob - stream @blob + send_blob_stream @blob end end end diff --git a/activestorage/app/controllers/active_storage/representations/proxy_controller.rb b/activestorage/app/controllers/active_storage/representations/proxy_controller.rb index 93943327f3..8ca0305cc2 100644 --- a/activestorage/app/controllers/active_storage/representations/proxy_controller.rb +++ b/activestorage/app/controllers/active_storage/representations/proxy_controller.rb @@ -3,12 +3,10 @@ # Proxy files through application. This avoids having a redirect and makes files easier to cache. class ActiveStorage::Representations::ProxyController < ActiveStorage::BaseController include ActiveStorage::SetBlob - include ActiveStorage::SetHeaders def show http_cache_forever public: true do - set_content_headers_from representation.image - stream representation + send_blob_stream representation.image end end diff --git a/activestorage/app/controllers/concerns/active_storage/set_headers.rb b/activestorage/app/controllers/concerns/active_storage/set_headers.rb deleted file mode 100644 index 7ab06bb14a..0000000000 --- a/activestorage/app/controllers/concerns/active_storage/set_headers.rb +++ /dev/null @@ -1,12 +0,0 @@ -# frozen_string_literal: true - -module ActiveStorage::SetHeaders #:nodoc: - extend ActiveSupport::Concern - - private - def set_content_headers_from(blob) - response.headers["Content-Type"] = blob.content_type_for_serving - response.headers["Content-Disposition"] = ActionDispatch::Http::ContentDisposition.format \ - disposition: blob.forced_disposition_for_serving || params[:disposition] || "inline", filename: blob.filename.sanitized - end -end diff --git a/activestorage/app/controllers/concerns/active_storage/streaming.rb b/activestorage/app/controllers/concerns/active_storage/streaming.rb new file mode 100644 index 0000000000..dc1707eb56 --- /dev/null +++ b/activestorage/app/controllers/concerns/active_storage/streaming.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module ActiveStorage::Streaming + DEFAULT_BLOB_STREAMING_DISPOSITION = "inline" + + include ActionController::Live + + private + # Stream the blob from storage directly to the response. The disposition can be controlled by setting +disposition+. + # The content type and filename is set directly from the +blob+. + def send_blob_stream(blob, disposition: nil) #:doc: + send_stream( + filename: blob.filename.sanitized, + disposition: blob.forced_disposition_for_serving || disposition || DEFAULT_BLOB_STREAMING_DISPOSITION, + type: blob.content_type_for_serving) do |stream| + blob.download do |chunk| + stream.write chunk + end + end + end +end diff --git a/activestorage/test/controllers/representations/proxy_controller_test.rb b/activestorage/test/controllers/representations/proxy_controller_test.rb index 5ed05c7afb..3ef5229d0a 100644 --- a/activestorage/test/controllers/representations/proxy_controller_test.rb +++ b/activestorage/test/controllers/representations/proxy_controller_test.rb @@ -48,7 +48,7 @@ class ActiveStorage::Representations::ProxyControllerWithPreviewsTest < ActionDi assert_predicate @blob.preview_image, :attached? - image = read_image(@blob.preview_image.variant(resize: "100x100")) + image = read_image(@blob.preview_image.variant(resize: "100x100").processed) assert_equal 77, image.width assert_equal 100, image.height end