Class: Tus::Storage::Filesystem

Inherits:
Object
  • Object
show all
Defined in:
lib/tus/storage/filesystem.rb

Defined Under Namespace

Classes: Response

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(directory, permissions: 0644, directory_permissions: 0755) ⇒ Filesystem

Returns a new instance of Filesystem.



13
14
15
16
17
18
19
# File 'lib/tus/storage/filesystem.rb', line 13

def initialize(directory, permissions: 0644, directory_permissions: 0755)
  @directory             = Pathname(directory)
  @permissions           = permissions
  @directory_permissions = directory_permissions

  create_directory! unless @directory.exist?
end

Instance Attribute Details

#directoryObject (readonly)

Returns the value of attribute directory.



11
12
13
# File 'lib/tus/storage/filesystem.rb', line 11

def directory
  @directory
end

Instance Method Details

#concatenate(uid, part_uids, info = {}) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/tus/storage/filesystem.rb', line 29

def concatenate(uid, part_uids, info = {})
  create_file(uid, info)

  file_path(uid).open("wb") do |file|
    part_uids.each do |part_uid|
      # Rather than checking upfront whether all parts exist, we use
      # exception flow to account for the possibility of parts being
      # deleted during concatenation.
      begin
        IO.copy_stream(file_path(part_uid), file)
      rescue Errno::ENOENT
        raise Tus::Error, "some parts for concatenation are missing"
      end
    end
  end

  # Delete parts after concatenation.
  delete(part_uids)

  # Tus server requires us to return the size of the concatenated file.
  file_path(uid).size
end

#create_file(uid, info = {}) ⇒ Object



21
22
23
24
25
26
27
# File 'lib/tus/storage/filesystem.rb', line 21

def create_file(uid, info = {})
  file_path(uid).binwrite("")
  file_path(uid).chmod(@permissions)

  info_path(uid).binwrite("{}")
  info_path(uid).chmod(@permissions)
end

#delete_file(uid, info = {}) ⇒ Object



89
90
91
# File 'lib/tus/storage/filesystem.rb', line 89

def delete_file(uid, info = {})
  delete([uid])
end

#expire_files(expiration_date) ⇒ Object



93
94
95
96
97
98
99
# File 'lib/tus/storage/filesystem.rb', line 93

def expire_files(expiration_date)
  uids = directory.children
    .select { |pathname| pathname.mtime <= expiration_date }
    .map { |pathname| pathname.basename(".*").to_s }

  delete(uids)
end

#get_file(uid, info = {}, range: nil) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/tus/storage/filesystem.rb', line 66

def get_file(uid, info = {}, range: nil)
  file = file_path(uid).open("rb")
  length = range ? range.size : file.size

  # Create an Enumerator which will yield chunks of the requested file
  # content, allowing tus server to efficiently stream requested content
  # to the client.
  chunks = Enumerator.new do |yielder|
    file.seek(range.begin) if range
    remaining_length = length

    while remaining_length > 0
      chunk = file.read([16*1024, remaining_length].min) or break
      remaining_length -= chunk.bytesize
      yielder << chunk
    end
  end

  # We return a response object that responds to #each, #length and #close,
  # which the tus server can return directly as the Rack response.
  Response.new(chunks: chunks, length: length, close: -> { file.close })
end

#patch_file(uid, input, info = {}) ⇒ Object



52
53
54
# File 'lib/tus/storage/filesystem.rb', line 52

def patch_file(uid, input, info = {})
  file_path(uid).open("ab") { |file| IO.copy_stream(input, file) }
end

#read_info(uid) ⇒ Object

Raises:



56
57
58
59
60
# File 'lib/tus/storage/filesystem.rb', line 56

def read_info(uid)
  raise Tus::NotFound if !file_path(uid).exist?

  JSON.parse(info_path(uid).binread)
end

#update_info(uid, info) ⇒ Object



62
63
64
# File 'lib/tus/storage/filesystem.rb', line 62

def update_info(uid, info)
  info_path(uid).binwrite(JSON.generate(info))
end