blobsend/blobsend/client_file.py

64 lines
2.0 KiB
Python

import os
from blobsend.client_base import BaseChunkClient
from blobsend import CHUNK_SIZE, hash_chunk
class FileChunkClient(BaseChunkClient):
def __init__(self, fpath, chunk_size=CHUNK_SIZE):
super().__init__(chunk_size)
self.fpath = fpath
self.file = open(self.fpath, "ab+") # for get chunk operations, this generic file is used instead of doing lots of open/close
self.file.seek(0)
def get_hashes(self):
i = 0
with open(self.fpath, "rb+") as f:
while True:
data = f.read(self.chunk_size)
if not data:
break
yield (i, hash_chunk(data))
i += 1
def get_chunk(self, chunk_number):
"""
return a file handle from which CHUNK_SIZE bytes of data can be read
"""
position = chunk_number * self.chunk_size
if position > os.path.getsize(self.fpath):#TODO not sure if > or >=
raise Exception("requested chunk {} is beyond EOF".format(chunk_number))
self.file.seek(position)#TODO not thread safe
return self.file.read(self.chunk_size)
def put_chunk(self, chunk_number, contents):
"""
insert the data for chunk_number's position within the file, the content given by contents (which is a file-like object) lol not actually
"""
position = chunk_number * self.chunk_size
with open(self.fpath, "rb+") as f:
f.seek(position)
f.write(contents)
def get_length(self):
"""
get the file size
"""
self.file.seek(0, 2) # seek to end
return self.file.tell()
def set_length(self, length):
if length < self.get_length():
self.file.truncate(length)
# do nothing for the case of extending the file
# put_chunk handles it
def close(self):
self.file.close()
@staticmethod
def from_uri(uri, is_src):
"""
instantiate a client from the given uri
"""
return FileChunkClient(uri.path)