-
Notifications
You must be signed in to change notification settings - Fork 21.6k
/
streaming.rb
65 lines (52 loc) · 2.19 KB
/
streaming.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# frozen_string_literal: true
require "securerandom"
module ActiveStorage::Streaming
DEFAULT_BLOB_STREAMING_DISPOSITION = "inline"
include ActionController::DataStreaming
include ActionController::Live
private
# Stream the blob in byte ranges specified through the header
def send_blob_byte_range_data(blob, range_header, disposition: nil)
ranges = Rack::Utils.get_byte_ranges(range_header, blob.byte_size)
return head(:range_not_satisfiable) if ranges.blank? || ranges.all?(&:blank?)
if ranges.length == 1
range = ranges.first
content_type = blob.content_type_for_serving
data = blob.download_chunk(range)
response.headers["Content-Range"] = "bytes #{range.begin}-#{range.end}/#{blob.byte_size}"
else
boundary = SecureRandom.hex
content_type = "multipart/byteranges; boundary=#{boundary}"
data = +""
ranges.compact.each do |range|
chunk = blob.download_chunk(range)
data << "\r\n--#{boundary}\r\n"
data << "Content-Type: #{blob.content_type_for_serving}\r\n"
data << "Content-Range: bytes #{range.begin}-#{range.end}/#{blob.byte_size}\r\n\r\n"
data << chunk
end
data << "\r\n--#{boundary}--\r\n"
end
response.headers["Accept-Ranges"] = "bytes"
response.headers["Content-Length"] = data.length.to_s
send_data(
data,
disposition: blob.forced_disposition_for_serving || disposition || DEFAULT_BLOB_STREAMING_DISPOSITION,
filename: blob.filename.sanitized,
status: :partial_content,
type: content_type
)
end
# 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