filestoraged/src/client/lib.cr

144 lines
3.8 KiB
Crystal

require "ipc"
class FileStorage::Client < IPC
property auth_token : String
def initialize(@auth_token, service_name = "filestorage")
super
@server_fd = self.connect service_name
end
# TODO: parse_message should raise exception if response not anticipated
def parse_message(expected_messages, message)
em = Array(IPC::JSON.class).new
expected_messages.each do |e|
em << e
end
em << FileStorage::Errors::GenericError
em << FileStorage::Errors::Authorization
em << FileStorage::Errors::ChunkAlreadyUploaded
em << FileStorage::Errors::ChunkUploadDenied
em << FileStorage::Errors::FileExists
em << FileStorage::Errors::FileDoesNotExist
em << FileStorage::Errors::FileFullyUploaded
em << FileStorage::Errors::FileTooBig
em.parse_ipc_json message
end
end
class FileStorage::Client < IPC
def login
request = FileStorage::Request::Login.new @auth_token
write @server_fd.not_nil!, request
parse_message [ FileStorage::Response::Login ], read
end
# Helper function.
def get_file_info(file_path : String)
file_info : FileStorage::FileInfo
file = File.open(file_path)
file_info = FileStorage::FileInfo.new file
file.close
file_info.not_nil!
end
def transfer(file_path : String)
file_info = get_file_info file_path
File.open(file_path) do |file|
buffer_size = FileStorage.message_buffer_size
buffer = Bytes.new buffer_size
counter = 0
size = 0
while (size = file.read(buffer)) > 0
# transfer message = file_info, chunk count, data (will be base64'd)
transfer_message = FileStorage::Request::PutChunk.new file_info,
counter,
buffer[0 ... size]
write @server_fd.not_nil!, transfer_message
counter += 1
buffer = Bytes.new buffer_size
# Check for the response
response = parse_message [ FileStorage::Response::PutChunk ], read
if response.responds_to? :mid
if response.mid != transfer_message.mid
raise "request and response mid !=: #{response.mid} != #{transfer_message.mid}"
else
pp! response
end
else
raise "response doesn't have mid"
end
end
end
end
def download(filedigest = nil, name = nil, tags = nil)
request = FileStorage::Request::Download.new filedigest, name, tags
write @server_fd.not_nil!, request
parse_message [ FileStorage::Response::Download ], read
end
def get_chunks(dl_response : FileStorage::Response::Download, path : String = ".")
file_path = "#{path}/#{dl_response.file_info.name}"
digest = dl_response.file_info.digest
buffer_size = FileStorage.message_buffer_size
counter = 0
size = 0
while counter < dl_response.file_info.nb_chunks
Baguette::Log.debug "getting #{file_path}: chunk #{counter+1}/#{dl_response.file_info.nb_chunks}"
get_chunk_message = FileStorage::Request::GetChunk.new digest, counter
write @server_fd.not_nil!, get_chunk_message
response = parse_message [ FileStorage::Response::GetChunk ], read
case response
when FileStorage::Response::GetChunk
b64_decoded_data = Base64.decode response.data
write_chunk file_path, buffer_size, response.chunk.n, b64_decoded_data
else
Baguette::Log.error "#{response}"
raise "wrong response: #{response}"
end
counter += 1
end
end
# Reception of a file chunk.
def write_chunk(file_path : String,
chunk_size : Int32,
offset : Int32,
data : Bytes
)
# pp! file_path, chunk_size, offset, data.size
Baguette::Log.debug "writing on #{file_path}"
# Create the file if non existant.
File.open(file_path, "a+") do |file|
file.seek (offset * chunk_size)
file.write data
end
end
def upload(file : String)
file_info : FileStorage::FileInfo
File.open(file) do |f|
file_info = FileStorage::FileInfo.new f
request = FileStorage::Request::Upload.new file_info
write @server_fd.not_nil!, request
end
parse_message [ FileStorage::Response::Upload ], read
end
end