Class: Service::QiniumService

Inherits:
Service
  • Object
show all
Defined in:
lib/active_storage/service/qinium_service.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ QiniumService

Returns a new instance of QiniumService.



16
17
18
# File 'lib/active_storage/service/qinium_service.rb', line 16

def initialize(options)
  @qiniu = Qinium.new(options)
end

Instance Attribute Details

#qiniu ⇒ Object (readonly)

Returns the value of attribute qiniu.



5
6
7
# File 'lib/active_storage/service/qinium_service.rb', line 5

def qiniu
  @qiniu
end

Class Method Details

.analyzers ⇒ Object



12
13
14
# File 'lib/active_storage/service/qinium_service.rb', line 12

def self.analyzers
  @analyzers ||= [ActiveStorage::Analyzer::QiniumImageAnalyzer]
end

Instance Method Details

#copy(source_bucket, source_key, target_bucket, target_key) ⇒ Object



119
120
121
122
123
124
# File 'lib/active_storage/service/qinium_service.rb', line 119

def copy(source_bucket, source_key, target_bucket, target_key)
  instrument :fetch, source_bucket: source_bucket, source_key: source_key, target_bucket: target_bucket,
                     target_key: target_key do
    qiniu.object.copy(source_bucket, source_key, target_bucket, target_key)
  end
end

#delete(key) ⇒ Object



101
102
103
104
105
# File 'lib/active_storage/service/qinium_service.rb', line 101

def delete(key)
  instrument :delete, key: key do
    qiniu.object.delete(key)
  end
end

#delete_prefixed(prefix) ⇒ Object



107
108
109
110
111
# File 'lib/active_storage/service/qinium_service.rb', line 107

def delete_prefixed(prefix)
  instrument :delete_prefixed, prefix: prefix do
    items_for(prefix).each { |item| delete item["key"] }
  end
end

#download(key) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/active_storage/service/qinium_service.rb', line 75

def download(key)
  if block_given?
    instrument :streaming_download, key: key do
      URI.open(url(key, disposition: :attachment)) do |file|
        while data = file.read(64.kilobytes)
          yield data
        end
      end
    end
  else
    instrument :download, key: key do
      URI.open(url(key, disposition: :attachment)).read
    end
  end
end

#download_chunk(key, range) ⇒ Object



91
92
93
94
95
96
97
98
99
# File 'lib/active_storage/service/qinium_service.rb', line 91

def download_chunk(key, range)
  instrument :download_chunk, key: key, range: range do
    uri = URI(url(key, disposition: :attachment))
    Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https") do |client|
      client.get(uri,
                 "Range" => "bytes=#{range.begin}-#{range.exclude_end? ? range.end - 1 : range.end}").body
    end
  end
end

#exist?(key) ⇒ Boolean

Returns:

  • (Boolean)


126
127
128
129
130
131
132
# File 'lib/active_storage/service/qinium_service.rb', line 126

def exist?(key)
  instrument :exist, key: key do |payload|
    answer = items_for(key).any?
    payload[:exist] = answer
    answer
  end
end

#fetch(target_url, key) ⇒ Object



113
114
115
116
117
# File 'lib/active_storage/service/qinium_service.rb', line 113

def fetch(target_url, key)
  instrument :fetch, target_url: target_url, key: key do
    qiniu.object.fetch(target_url, key)
  end
end

#form_data_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:) ⇒ Object



36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/active_storage/service/qinium_service.rb', line 36

def form_data_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:, **)
  put_policy = Qinium::PutPolicy.new(config, key: key, expires_in: expires_in)
  put_policy.fsize_limit = content_length.to_i + 1000
  # OPTIMIZE: 暂时关闭文件类型限制,避免 xmind 文件无法上传
  put_policy.mime_limit = nil
  put_policy.detect_mime = 1
  put_policy.insert_only = 1
  {
    key: key,
    token: put_policy.to_token,
    ':file': "file"
  }
end

#http_method_for_direct_upload ⇒ Object



28
29
30
# File 'lib/active_storage/service/qinium_service.rb', line 28

def http_method_for_direct_upload
  "POST"
end

#http_response_type_for_direct_upload ⇒ Object



32
33
34
# File 'lib/active_storage/service/qinium_service.rb', line 32

def http_response_type_for_direct_upload
  "json"
end

#update_metadata(key, **metadata) ⇒ Object



73
# File 'lib/active_storage/service/qinium_service.rb', line 73

def (key, **); end

#upload(key, io, checksum: nil, content_type: nil) ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/active_storage/service/qinium_service.rb', line 50

def upload(key, io, checksum: nil, content_type: nil, **)
  instrument :upload, key: key, checksum: checksum do
    io = File.open(io) unless io.respond_to?(:read)

    put_policy = Qinium::PutPolicy.new(config, key: key, expires_in: put_policy_options.expires_in)
    up_token = put_policy.to_token
    blocks = []
    file_size = 0
    host = nil
    while (blk = io.read(config.block_size))
      data = upload_blk(blk, token: up_token, host: host)
      ctx = data.fetch("ctx")
      host = data.fetch("host")
      file_size += blk.size
      blocks.push(ctx)
    end

    _code, data, _headers = qiniu.object.mkfile(token: up_token, file_size: file_size, key: key,
                                                mime_type: content_type, blocks: blocks)
    data
  end
end

#url(key, **options) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/active_storage/service/qinium_service.rb', line 134

def url(key, **options)
  instrument :url, key: key do |payload|
    # 根据七牛云文档:https://developer.qiniu.com/kodo/1659/download-setting
    # 1. 使用 attname 参数可以让浏览器下载而不是打开
    # 2. 使用 response-content-disposition 参数可以自定义 Content-Disposition 响应头
    # 3. 使用 response-content-type 参数可以自定义 Content-Type 响应头
    # 4. 使用 response-cache-control 参数可以自定义 Cache-Control 响应头
    # 5. 使用 response-content-encoding 参数可以自定义 Content-Encoding 响应头
    # 6. 使用 response-content-language 参数可以自定义 Content-Language 响应头
    # 7. 使用 response-expires 参数可以自定义 Expires 响应头
    # 8. 使用 X-Qiniu-Traffic-Limit 参数可以限制下载速度

    disposition = options[:disposition]
    content_type = options[:content_type]

    # 构建查询参数
    query_params = []

    # 内容预处理(图片处理、视频处理等)
    query_params << options[:fop] if options[:fop].present?

    # 处理 disposition 相关参数
    if disposition.to_s == "attachment" # 下载附件
      # attname:触发「下载」行为;部分节点仍用对象 key 填 Content-Disposition,须同时传 response-content-disposition。
      # 文档:https://developer.qiniu.com/kodo/1659/download-setting
      display_name = (options[:filename].presence || File.basename(key)).to_s
      query_params << "attname=#{URI.encode_www_form_component(display_name)}"
      # cd = ActionDispatch::Http::ContentDisposition.format(
      #   disposition: "attachment",
      #   filename: ActiveStorage::Filename.new(display_name).sanitized
      # )
      # query_params << "response-content-disposition=#{ERB::Util.url_encode(cd)}"
      # elsif disposition.to_s == "inline" # 预览(inline)
      #   # 明确设置 Content-Disposition 为 inline,确保浏览器预览而不是下载
      #   query_params << "response-content-disposition=inline"
    end

    # 自定义响应头参数
    # 注意:这些参数只有请求成功(即返回码为 200 OK)才会生效
    # 且不支持在匿名访问的下载请求中自定义标准响应头
    if options[:response_content_type].present? || content_type.present?
      value = options[:response_content_type] || content_type
      query_params << "response-content-type=#{URI.encode_www_form_component(value)}"
    end

    if options[:response_cache_control].present?
      query_params << "response-cache-control=#{URI.encode_www_form_component(options[:response_cache_control])}"
    end

    if options[:response_content_disposition].present?
      query_params << "response-content-disposition=#{ERB::Util.url_encode(options[:response_content_disposition])}"
    end

    if options[:response_content_encoding].present?
      query_params << "response-content-encoding=#{URI.encode_www_form_component(options[:response_content_encoding])}"
    end

    if options[:response_content_language].present?
      query_params << "response-content-language=#{URI.encode_www_form_component(options[:response_content_language])}"
    end

    if options[:response_expires].present?
      query_params << "response-expires=#{URI.encode_www_form_component(options[:response_expires])}"
    end

    # 下载限速:X-Qiniu-Traffic-Limit
    # 取值范围为 819200 ~ 838860800,单位为 bit/s
    query_params << "X-Qiniu-Traffic-Limit=#{options[:traffic_limit].to_i}" if options[:traffic_limit].present?

    # 构建 URL
    url = if config.public
            url_encoded_key = key.split("/").map { |x| CGI.escape(x) }.join("/")
            base_url = "#{protocol}://#{domain}/#{url_encoded_key}"
            query_params.any? ? "#{base_url}?#{query_params.join("&")}" : base_url
          else
            expires_in = options[:expires_in] ||
                         Rails.application.config.active_storage.service_urls_expire_in ||
                         3600
            # 对于私有资源,需要将查询参数传递给授权 URL 生成
            fop = query_params.join("&") if query_params.any?
            Qinium::Auth.authorize_download_url(domain, key,
                                                access_key, secret_key,
                                                schema: protocol, fop: fop, expires_in: expires_in)
          end

    payload[:url] = url
    url
  end
end

#url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:, custom_metadata:) ⇒ Object



20
21
22
23
24
25
26
# File 'lib/active_storage/service/qinium_service.rb', line 20

def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:, custom_metadata:)
  instrument :url, key: key do |payload|
    url = config.up_host
    payload[:url] = url
    url
  end
end