blobsend/blobsend/client_file.py

76 lines
2.4 KiB
Python

import os
from blobsend.client_base import BaseChunkClient
from blobsend import CHUNK_SIZE, hash_chunk, FilePool
class FileChunkClient(BaseChunkClient):
def __init__(self, fpath, is_src, chunk_size=CHUNK_SIZE):
super().__init__(chunk_size)
self.fpath = fpath
if not is_src and not os.path.exists(self.fpath):
with open(self.fpath, "wb"):
pass
def _fpool_open():
f = open(self.fpath, "rb+") # for get chunk operations, this generic file is used instead of doing lots of open/close
f.seek(0)
return f
self.fpool = FilePool(10, _fpool_open)
def get_hashes(self):
i = 0
with self.fpool.get() 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))
with self.fpool.get() as f:
f.seek(position)#TODO not thread safe
return f.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
"""
with self.fpool.get() as f:
f.seek(0, 2) # seek to end
return f.tell()
def set_length(self, length):
if length < self.get_length():
with self.fpool.get() as f:
f.truncate(length)
# do nothing for the case of extending the file
# put_chunk handles it
def close(self):
self.fpool.close()
@staticmethod
def from_uri(uri, extra_args, is_src):
"""
instantiate a client from the given uri
"""
return FileChunkClient(uri.path, is_src)