class ActiveStorage::Service::QiniuService

Wraps the Qiniu Storage Service as an Active Storage service. See ActiveStorage::Service for the generic API documentation that applies to all services.

you can set-up qiniu storage service through the generated <tt>config/storage.yml</tt> file.
For example:

 qiniu:
   service: Qiniu
   access_key: <%= ENV['QINIU_ACCESS_KEY'] %>
   secret_key: <%= ENV['QINIU_SECRET_KEY'] %>
   bucket: <%= ENV['QINIU_BUCKET'] %>
   domain: <%= ENV['QINIU_DOMAIN'] %>
   protocol: <%= ENV.fetch("QINIU_PROTOCOL") { "http" } %>

more options. https://github.com/qiniu/ruby-sdk/blob/master/lib/qiniu/auth.rb#L49

Then, in your application's configuration, you can specify the service to use like this:

config.active_storage.service = :qiniu

Constants

BLOCK_SIZE

Attributes

bucket[R]
bucket_private[R]
domain[R]
protocol[R]
upload_options[R]

Public Class Methods

new(access_key:, secret_key:, bucket:, domain:, **options) click to toggle source
# File lib/active_storage/service/qiniu_service.rb, line 31
def initialize(access_key:, secret_key:, bucket:, domain:, **options)
  @bucket = bucket
  @domain = domain
  @protocol = (options.delete(:protocol) || 'https').to_sym
  bucket_private = options.delete(:bucket_private)
  @bucket_private = bucket_private.nil? ? false : !!bucket_private
  Qiniu.establish_connection! access_key: access_key,
                              secret_key: secret_key,
                              protocol: @protocol,
                              **options

  @upload_options = options
end

Public Instance Methods

delete(key) click to toggle source
# File lib/active_storage/service/qiniu_service.rb, line 71
def delete(key)
  instrument :delete, key: key do
    Qiniu.delete(bucket, key)
  end
end
delete_prefixed(prefix) click to toggle source
# File lib/active_storage/service/qiniu_service.rb, line 77
def delete_prefixed(prefix)
  instrument :delete_prefixed, prefix: prefix do
    items_for(prefix).each { |item| delete item['key'] }
  end
end
download(key) { |data| ... } click to toggle source
# File lib/active_storage/service/qiniu_service.rb, line 91
def download(key)
  if block_given?
    instrument :streaming_download, key: key do
      open(url(key, disposition: :attachment)) do |file|
        while data = file.read(64.kilobytes)
          yield data
        end
      end
    end
  else
    instrument :download, key: key do
      open(url(key, disposition: :attachment)).read
    end
  end
end
download_chunk(key, range) click to toggle source
# File lib/active_storage/service/qiniu_service.rb, line 107
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) click to toggle source
# File lib/active_storage/service/qiniu_service.rb, line 83
def exist?(key)
  instrument :exist, key: key do |payload|
    answer = items_for(key).any?
    payload[:exist] = answer
    answer
  end
end
headers_for_direct_upload(key, content_type:, checksum:, **) click to toggle source
# File lib/active_storage/service/qiniu_service.rb, line 146
def headers_for_direct_upload(key, content_type:, checksum:, **)
  { "Content-Type" => content_type, "Content-MD5" => checksum, "x-token" => generate_uptoken(key) }
end
upload(key, io, checksum: nil, content_type: nil, **) click to toggle source
# File lib/active_storage/service/qiniu_service.rb, line 45
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)

    ctx_list = []
    file_size = 0
    while (blk = io.read(BLOCK_SIZE))
      ctx = upload_blk(key, blk)
      file_size += blk.size
      ctx_list.push(ctx)
    end

    api_call(
      key,
      '/' + [
        'mkfile',
        file_size,
        'key',
        encode(key),
        *(content_type ? ['mimeType', encode(content_type)] : [])
      ].join('/'),
      ctx_list.join(',')
    )
  end
end
url(key, **options) click to toggle source
# File lib/active_storage/service/qiniu_service.rb, line 116
def url(key, **options)
  instrument :url, key: key do |payload|
    fop = if options[:fop].present?        # 内容预处理
            options[:fop]
          elsif options[:disposition].to_s == 'attachment' # 下载附件
            attname = URI.escape "#{options[:filename] || key}"
            "attname=#{attname}"
          end

    url = if bucket_private
            expires_in = options[:expires_in] || url_expires_in
            Qiniu::Auth.authorize_download_url_2(domain, key, schema: protocol, fop: fop, expires_in: expires_in)
          else
            url_encoded_key = CGI::escape(key)
            "#{protocol}://#{domain}/#{url_encoded_key}?#{fop}"
          end

    payload[:url] = url
    url
  end
end
url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:) click to toggle source
# File lib/active_storage/service/qiniu_service.rb, line 138
def url_for_direct_upload(key, expires_in:, content_type:, content_length:, checksum:)
  instrument :url, key: key do |payload|
    url = Qiniu::Config.up_host(bucket)
    payload[:url] = url
    url
  end
end

Private Instance Methods

api_call(key, path, body) click to toggle source
# File lib/active_storage/service/qiniu_service.rb, line 181
def api_call(key, path, body)
  url = Qiniu::Config.up_host(bucket) + path

  response = RestClient.post(
    url,
    body,
    'Authorization' => "UpToken #{generate_uptoken(key)}"
  )

  result = JSON.parse(response.body)

  result
end
encode(value) click to toggle source
# File lib/active_storage/service/qiniu_service.rb, line 195
def encode(value)
  Base64.encode64(value).strip.gsub(/\+/, '-').gsub(%r{/}, '_')
end
generate_uptoken(key=nil, expires_in=nil) click to toggle source
# File lib/active_storage/service/qiniu_service.rb, line 163
def generate_uptoken(key=nil, expires_in=nil)
  expires_in ||= 3600
  put_policy = Qiniu::Auth::PutPolicy.new(bucket, key, expires_in)
  upload_options.slice(*Qiniu::Auth::PutPolicy::PARAMS.keys).each do |k, v|
    put_policy.send("#{k}=", v)
  end

  Qiniu::Auth.generate_uptoken(put_policy)
end
items_for(prefix='') click to toggle source
# File lib/active_storage/service/qiniu_service.rb, line 152
def items_for(prefix='')
  list_policy = Qiniu::Storage::ListPolicy.new(
    bucket,   # 存储空间
    1000,     # 列举的条目数
    prefix,   # 指定前缀
    ''        # 指定目录分隔符
  )
  code, result, response_headers, s, d = Qiniu::Storage.list(list_policy)
  result['items']
end
upload_blk(key, blk) click to toggle source
# File lib/active_storage/service/qiniu_service.rb, line 173
def upload_blk(key, blk)
  result =
    with_retries(max_retries: 3) do
      api_call(key, "/mkblk/#{blk.size}", blk)
    end
  result.fetch('ctx')
end