class Trove::Storage::S3

Attributes

bucket[R]
prefix[R]

Public Class Methods

new(bucket:, prefix: nil) click to toggle source
# File lib/trove/storage/s3.rb, line 9
def initialize(bucket:, prefix: nil)
  @bucket = bucket
  @prefix = prefix
end

Public Instance Methods

delete(filename, version: nil) click to toggle source
# File lib/trove/storage/s3.rb, line 66
def delete(filename, version: nil)
  options = {bucket: bucket, key: key(filename)}
  options[:version_id] = version if version
  client.delete_object(**options)
  true
rescue Aws::S3::Errors::ServiceError
  false
end
download(filename, dest, version: nil) { |current_size, total_size| ... } click to toggle source
# File lib/trove/storage/s3.rb, line 14
def download(filename, dest, version: nil)
  current_size = 0
  total_size = nil

  # TODO better path
  tmp = "#{Dir.tmpdir}/trove-#{Time.now.to_f}"
  begin
    File.open(tmp, "wb") do |file|
      options = {bucket: bucket, key: key(filename)}
      options[:version_id] = version if version
      client.get_object(**options) do |chunk, headers|
        file.write(chunk)

        current_size += chunk.bytesize
        total_size ||= headers["content-length"].to_i
        yield current_size, total_size
      end
    end
    FileUtils.mv(tmp, dest)
  ensure
    # delete file if interrupted
    File.unlink(tmp) if File.exist?(tmp)
  end
rescue Aws::S3::Errors::ServiceError
  raise "File not found"
end
info(filename, version: nil) click to toggle source

etag isn't always MD5, but low likelihood of match if not could alternatively add sha256 to metadata

# File lib/trove/storage/s3.rb, line 54
def info(filename, version: nil)
  options = {bucket: bucket, key: key(filename)}
  options[:version_id] = version if version
  resp = client.head_object(**options)
  {
    version: resp.version_id,
    md5: resp.etag.gsub('"', "")
  }
rescue Aws::S3::Errors::ServiceError
  nil
end
list() click to toggle source
# File lib/trove/storage/s3.rb, line 75
def list
  files = []
  options = {bucket: bucket}
  options[:prefix] = prefix if prefix
  client.list_objects_v2(**options).each do |response|
    response.contents.each do |object|
      filename = prefix ? object.key[(prefix.size + 1)..-1] : object.key
      files << {
        filename: filename,
        size: object.size,
        updated_at: object.last_modified
      }
    end
  end
  files
end
upload(src, filename, &block) click to toggle source
# File lib/trove/storage/s3.rb, line 41
def upload(src, filename, &block)
  on_chunk_sent = lambda do |_, current_size, total_size|
    block.call(current_size, total_size)
  end
  resp = nil
  File.open(src, "rb") do |file|
    resp = client.put_object(bucket: bucket, key: key(filename), body: file, on_chunk_sent: on_chunk_sent)
  end
  {version: resp.version_id}
end
versions(filename) click to toggle source
# File lib/trove/storage/s3.rb, line 92
def versions(filename)
  versions = []
  object_key = key(filename)
  client.list_object_versions(bucket: bucket, prefix: object_key).each do |response|
    response.versions.each do |version|
      next if version.key != object_key

      versions << {
        version: version.version_id == "null" ? nil : version.version_id,
        size: version.size,
        updated_at: version.last_modified
      }
    end
  end
  versions
end

Private Instance Methods

client() click to toggle source
# File lib/trove/storage/s3.rb, line 111
def client
  @client ||= Aws::S3::Client.new
end
key(filename) click to toggle source
# File lib/trove/storage/s3.rb, line 115
def key(filename)
  prefix ? "#{prefix}/#{filename}" : filename
end