Class: Shrine::Storage::GoogleCloudStorage

Inherits:
Object
  • Object
show all
Defined in:
lib/shrine/storage/google_cloud_storage.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(bucket:, prefix: nil, host: nil, default_acl: nil, object_options: {}) ⇒ GoogleCloudStorage

Returns a new instance of GoogleCloudStorage.



10
11
12
13
14
15
16
# File 'lib/shrine/storage/google_cloud_storage.rb', line 10

def initialize(bucket:, prefix: nil, host: nil, default_acl: nil, object_options: {})
  @bucket = bucket
  @prefix = prefix
  @host = host
  @default_acl = default_acl
  @object_options = object_options
end

Instance Attribute Details

#bucketObject (readonly)

Returns the value of attribute bucket.



8
9
10
# File 'lib/shrine/storage/google_cloud_storage.rb', line 8

def bucket
  @bucket
end

#hostObject (readonly)

Returns the value of attribute host.



8
9
10
# File 'lib/shrine/storage/google_cloud_storage.rb', line 8

def host
  @host
end

#prefixObject (readonly)

Returns the value of attribute prefix.



8
9
10
# File 'lib/shrine/storage/google_cloud_storage.rb', line 8

def prefix
  @prefix
end

Instance Method Details

#clear!Object



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'lib/shrine/storage/google_cloud_storage.rb', line 94

def clear!
  ids = []

  storage_api.fetch_all do |token, s|
    prefix = "#{@prefix}/" if @prefix
    s.list_objects(
      @bucket,
      prefix: prefix,
      fields: "items/name",
      page_token: token,
    )
  end.each do |object|
    ids << object.name

    if ids.size >= 100
      # Batches are limited to 100, so we execute it and reset the ids
      batch_delete(ids)
      ids = []
    end
  end

  # We delete the remaining ones
  batch_delete(ids) unless ids.empty?
end

#delete(id) ⇒ Object



79
80
81
82
83
84
85
86
87
88
# File 'lib/shrine/storage/google_cloud_storage.rb', line 79

def delete(id)
  # deletes the file from the storage
  storage_api.delete_object(@bucket, object_name(id))

rescue Google::Apis::ClientError => e
  # The object does not exist, Shrine expects us to be ok
  return true if e.status_code == 404

  raise e
end

#download(id) ⇒ Object



51
52
53
54
55
# File 'lib/shrine/storage/google_cloud_storage.rb', line 51

def download(id)
  tempfile = Tempfile.new(["googlestorage", File.extname(id)], binmode: true)
  storage_api.get_object(@bucket, object_name(id), download_dest: tempfile)
  tempfile.tap(&:open)
end

#exists?(id) ⇒ Boolean

Returns:

  • (Boolean)


64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/shrine/storage/google_cloud_storage.rb', line 64

def exists?(id)
  # checks if the file exists on the storage
  storage_api.get_object(@bucket, object_name(id)) do |_, err|
    if err
      if err.status_code == 404
        false
      else
        raise err
      end
    else
      true
    end
  end
end

#multi_delete(ids) ⇒ Object



90
91
92
# File 'lib/shrine/storage/google_cloud_storage.rb', line 90

def multi_delete(ids)
  batch_delete(ids.map { |i| object_name(i) })
end

#object_name(id) ⇒ Object



142
143
144
# File 'lib/shrine/storage/google_cloud_storage.rb', line 142

def object_name(id)
  @prefix ? "#{@prefix}/#{id}" : id
end

#open(id) ⇒ Object



57
58
59
60
61
62
# File 'lib/shrine/storage/google_cloud_storage.rb', line 57

def open(id)
  # returns the remote file as an IO-like object
  io = storage_api.get_object(@bucket, object_name(id), download_dest: StringIO.new)
  io.rewind
  io
end

#presign(id, **options) ⇒ Object



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/shrine/storage/google_cloud_storage.rb', line 119

def presign(id, **options)
  method = options[:method] || "GET"
  content_md5 = options[:content_md5] || ""
  content_type = options[:content_type] || ""
  expires = (Time.now.utc + (options[:expires] || 300)).to_i
  headers = nil
  path = "/#{@bucket}/" + object_name(id)

  to_sign = [method, content_md5, content_type, expires, headers, path].compact.join("\n")

  signing_key = options[:signing_key]
  signing_key = OpenSSL::PKey::RSA.new(signing_key) unless signing_key.respond_to?(:sign)
  signature = Base64.strict_encode64(signing_key.sign(OpenSSL::Digest::SHA256.new, to_sign)).delete("\n")

  signed_url = "https://storage.googleapis.com#{path}?GoogleAccessId=#{options[:issuer]}" \
    "&Expires=#{expires}&Signature=#{CGI.escape(signature)}"

  OpenStruct.new(
    url: signed_url,
    fields: {},
  )
end

#upload(io, id, shrine_metadata: {}, **_options) ⇒ Object



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
# File 'lib/shrine/storage/google_cloud_storage.rb', line 18

def upload(io, id, shrine_metadata: {}, **_options)
  # uploads `io` to the location `id`

  object = Google::Apis::StorageV1::Object.new @object_options.merge(bucket: @bucket, name: object_name(id))

  if copyable?(io)
    storage_api.copy_object(
      io.storage.bucket,
      io.storage.object_name(io.id),
      @bucket,
      object_name(id),
      object,
      destination_predefined_acl: @default_acl,
    )
  else
    storage_api.insert_object(
      @bucket,
      object,
      content_type: ["mime_type"],
      upload_source: io.to_io,
      options: { uploadType: 'multipart' },
      predefined_acl: @default_acl,
    )
  end
end

#url(id, **_options) ⇒ Object



44
45
46
47
48
49
# File 'lib/shrine/storage/google_cloud_storage.rb', line 44

def url(id, **_options)
  # URL to the remote file, accepts options for customizing the URL
  host = @host || "storage.googleapis.com/#{@bucket}"

  "https://#{host}/#{object_name(id)}"
end