# frozen_string_literal: true require 'fog/core/collection' require 'fog/aliyun/models/storage/file' require 'aliyun/oss' module Fog module Aliyun class Storage class Files < Fog::Collection attribute :directory attribute :limit attribute :prefix, :aliases => 'Prefix' attribute :path attribute :common_prefixes, :aliases => 'CommonPrefixes' attribute :delimiter, :aliases => 'Delimiter' attribute :is_truncated, :aliases => 'IsTruncated' attribute :marker, :aliases => 'Marker' attribute :max_keys, :aliases => ['MaxKeys', 'max-keys'] model Fog::Aliyun::Storage::File # check_directory_key have two functions: # 1. trim the directory_key suffix '/' # 2. checking whether the directory_key is a bucket. # If so, it will return directly to avoid to create a new redundant folder named with directory_key. # This point will be applied to multi-bucket and make bucket as a directory scenario. def check_directory_key(directory_key) bucket_name = nil if directory_key.is_a? Array directory_key = directory_key[0] end if directory_key != '' # trim the suffix '/' directory_key = directory_key.chomp('/') # The bucket name can not contain '/', so if directory_key, return directory. if directory_key.include? '/' directory_key else data = service.get_bucket(directory_key) if data.class == Hash && data.key?('Code') && !data['Code'].nil? && !data['Code'].empty? directory_key else bucket_name = directory_key directory_key = '' end end end return bucket_name, directory_key end def all(options = {}) requires :directory bucket_name, directory_key = check_directory_key(directory.key) remap_attributes(options, { :delimiter => 'delimiter', :marker => 'marker', :max_keys => 'max-keys', :prefix => 'prefix' }) prefix_value = options['prefix'] prefix_value = directory_key + '/' + prefix if directory_key != '' && directory_key != '.' && !directory_key.nil? options['prefix'] = prefix_value options['bucket'] = bucket_name files = service.list_objects(options)['Contents'] return if files.nil? data = [] i = 0 files.each do |file| next unless file['Key'][0][-1] != '/' content_length = file['Size'][0].to_i key = file['Key'][0] lastModified = file['LastModified'][0] last_modified = (Time.parse(lastModified).localtime if !lastModified.nil? && lastModified != '') type = file['Type'][0] data[i] = { content_length: content_length, key: key, last_modified: last_modified, etag: file['ETag'][0], object_type: type } i += 1 end load(data) end alias each_file_this_page each def each if !block_given? self else subset = dup.all subset.each_file_this_page { |f| yield f } while subset.length == (subset.limit || 10_000) subset = subset.all(marker: subset.last.key) subset.each_file_this_page { |f| yield f } end self end end def get(key) requires :directory bucket_name, directory_key = check_directory_key(directory.key) object = if directory_key == '' key else directory_key + '/' + key end begin data = service.get_object(object, nil, bucket: bucket_name) lastModified = data['headers'][:last_modified] last_modified = (Time.parse(lastModified).localtime if !lastModified.nil? && lastModified != '') date = data['headers'][:date] date = (Time.parse(date).localtime if !date.nil? && date != '') file_data = { body: data[:body], content_length: data['headers'][:content_length].to_i, key: key, last_modified: last_modified, content_type: data['headers'][:content_type], etag: data['headers'][:etag], date: date, connection: data['headers'][:connection], accept_ranges: data['headers'][:accept_ranges], server: data['headers'][:server], object_type: data['headers'][:x_oss_object_type] } new(file_data) rescue AliyunOssSdk::ServerError => error case error.error_code when %r{NoSuchKey},%r{SymlinkTargetNotExist} nil else raise(error) end end end def get_url(key) requires :directory bucket_name, directory_key = check_directory_key(directory.key) object = if directory_key == '' key else directory_key + '/' + key end service.get_object_http_url_public(object, 3600, bucket: bucket_name) end def get_http_url(key, expires, options = {}) requires :directory bucket_name, directory_key = check_directory_key(directory.key) object = if directory_key == '' key else directory_key + '/' + key end expires = expires.nil? ? 0 : expires.to_i service.get_object_http_url_public(object, expires, options.merge(bucket: bucket_name)) end def get_https_url(key, expires, options = {}) requires :directory bucket_name, directory_key = check_directory_key(directory.key) object = if directory_key == '' key else directory_key + '/' + key end expires = expires.nil? ? 0 : expires.to_i service.get_object_https_url_public(object, expires, options.merge(bucket: bucket_name)) end def head(key, _options = {}) requires :directory bucket_name, directory_key = check_directory_key(directory.key) object = if directory_key == '' key else directory_key + '/' + key end data = service.head_object(object, bucket: bucket_name).data return nil if data[:status] == 404 lastModified = data[:headers]['Last-Modified'] last_modified = (Time.parse(lastModified).localtime if !lastModified.nil? && lastModified != '') date = data[:headers]['Date'] date = (Time.parse(date).localtime if !date.nil? && date != '') file_data = { content_length: data[:headers]['Content-Length'].to_i, key: key, last_modified: last_modified, content_type: data[:headers]['Content-Type'], etag: data[:headers]['ETag'], date: date, connection: data[:headers]['Connection'], accept_ranges: data[:headers]['Accept-Ranges'], server: data[:headers]['Server'], object_type: data[:headers]['x-oss-object-type'] } new(file_data) rescue Fog::Aliyun::Storage::NotFound nil end def new(attributes = {}) requires :directory # Sometimes, the v will be a Array, like "Prefix"=>[{}], "Marker"=>[xxxx], "MaxKeys"=>["100"], "IsTruncated"=>["false"] # and there needs to parse them for k, v in attributes if !v.nil? && (v.is_a? Array) && (v.size > 0) attributes[k] = v[0] end end super({ directory: directory }.merge!(attributes)) end end end end end