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

Initializes the storage with a directory, in which it will save all files. Creates the directory if it doesn’t exist.



17
18
19
20
21
22
23
# File 'lib/tus/storage/filesystem.rb', line 17

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.



13
14
15
# File 'lib/tus/storage/filesystem.rb', line 13

def directory
  @directory
end

Instance Method Details

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

Concatenates multiple partial uploads into a single upload, and returns the size of the resulting upload. The partial uploads are deleted after concatenation.

Raises Tus::Error if any partial upload is missing.



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/tus/storage/filesystem.rb', line 39

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

Creates a file for storing uploaded data and a file for storing info.



26
27
28
29
30
31
32
# File 'lib/tus/storage/filesystem.rb', line 26

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

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

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

Deletes data and info files for the specified upload.



106
107
108
# File 'lib/tus/storage/filesystem.rb', line 106

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

#expire_files(expiration_date) ⇒ Object

Deletes data and info files of uploads older than the specified date.



111
112
113
114
115
116
117
# File 'lib/tus/storage/filesystem.rb', line 111

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

Returns a Tus::Response object through which data of the specified upload can be retrieved in a streaming fashion. Accepts an optional range parameter for selecting a subset of bytes to retrieve.



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/tus/storage/filesystem.rb', line 84

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

  Response.new(chunks: chunks, close: file.method(:close), path: file_path(uid).to_s)
end

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

Appends data to the specified upload in a streaming fashion, and returns the number of bytes it managed to save.



64
65
66
# File 'lib/tus/storage/filesystem.rb', line 64

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

#read_info(uid) ⇒ Object

Returns info of the specified upload. Raises Tus::NotFound if the upload wasn’t found.

Raises:



70
71
72
73
74
# File 'lib/tus/storage/filesystem.rb', line 70

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

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

#update_info(uid, info) ⇒ Object

Updates info of the specified upload.



77
78
79
# File 'lib/tus/storage/filesystem.rb', line 77

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