2021-11-25 09:40:25 +00:00
|
|
|
from const import STATUS_COMPLETE, STATUS_START
|
|
|
|
|
2021-11-20 14:40:07 +00:00
|
|
|
|
|
|
|
class RemoteFile:
|
2021-11-25 15:31:49 +00:00
|
|
|
"""Class describing the transfer status of a File to a Vessel
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, fileobj, vessel, chunksize: int) -> None:
|
|
|
|
"""Initialize a new RemoteFile object
|
|
|
|
|
|
|
|
Args:
|
|
|
|
fileobj (classes.file.File): File object to transfer to a Vessel
|
|
|
|
vessel (classes.vessel.Vessel): Vessel to transfer the File to
|
|
|
|
chunksize (int): Size of a single Chunk to transfer
|
|
|
|
"""
|
2021-11-20 14:40:07 +00:00
|
|
|
self.file = fileobj
|
|
|
|
self.vessel = vessel
|
|
|
|
self.chunksize = chunksize
|
|
|
|
|
2021-11-25 15:31:49 +00:00
|
|
|
def getStatus(self) -> int:
|
|
|
|
"""Get the current transfer status
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
int: Number of the last Chunk that was uploaded, or STATUS_COMPLETE
|
|
|
|
(-1) if a file upload is complete and waiting for finalization,
|
|
|
|
or STATUS_START (-2) if no Chunk has been uploaded yet
|
|
|
|
"""
|
|
|
|
|
|
|
|
# Get all files in the vessel's tempdir
|
|
|
|
|
2021-11-22 10:14:38 +00:00
|
|
|
ls = self.vessel.connection._listdir(self.vessel.tempdir)
|
2021-11-25 09:40:25 +00:00
|
|
|
files = [f for f in ls if f.startswith(
|
|
|
|
self.file.uuid) and f.endswith(".part")]
|
2021-11-20 14:40:07 +00:00
|
|
|
|
2021-11-25 15:31:49 +00:00
|
|
|
# Find the file with the largest chunk number
|
|
|
|
|
|
|
|
count = -1
|
2021-11-20 14:40:07 +00:00
|
|
|
|
|
|
|
for f in files:
|
|
|
|
part = f.split("_")[1].split(".")[0]
|
2021-11-25 15:31:49 +00:00
|
|
|
if part == "complete": # If a reassembled file is found
|
|
|
|
if self.validateComplete(True): # and it is not broken
|
|
|
|
return STATUS_COMPLETE # the upload is complete
|
2021-11-20 14:40:07 +00:00
|
|
|
|
2021-11-25 15:31:49 +00:00
|
|
|
# Else save the chunk number if it is larger than the previous
|
|
|
|
count = max(count, int(part))
|
2021-11-20 14:40:07 +00:00
|
|
|
|
2021-11-25 15:31:49 +00:00
|
|
|
# Find and return the largest non-corrupt chunk
|
2021-11-20 14:40:07 +00:00
|
|
|
while count >= 0:
|
|
|
|
if self.validateChunk(count):
|
|
|
|
return count
|
2021-11-25 09:40:25 +00:00
|
|
|
count -= 1
|
|
|
|
|
2021-11-25 15:31:49 +00:00
|
|
|
# If no (more) files exist, we are just getting started
|
2021-11-20 14:40:07 +00:00
|
|
|
return STATUS_START
|
|
|
|
|
2021-11-25 15:31:49 +00:00
|
|
|
def validateChunk(self, count: int) -> bool:
|
|
|
|
"""Validate that a Chunk was uploaded correctly
|
|
|
|
|
|
|
|
Args:
|
|
|
|
count (int): Chunk number to validate
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
bool: True if file has been uploaded correctly, else False
|
|
|
|
"""
|
2021-11-20 14:40:07 +00:00
|
|
|
return self.vessel.connection.assertChunkComplete(self.getChunk(count))
|
|
|
|
|
2021-11-25 15:31:49 +00:00
|
|
|
def validateComplete(self, allow_retry: bool = False):
|
|
|
|
"""Validate that the complete File was reassembled correctly
|
|
|
|
|
|
|
|
Args:
|
|
|
|
allow_retry (bool, optional): If True, assume that compileComplete
|
|
|
|
failed for some other reason than corrupt Chunks, and only delete
|
|
|
|
compiled file, else clear entire temporary directory. Defaults to
|
|
|
|
False.
|
2021-11-20 14:40:07 +00:00
|
|
|
|
2021-11-25 15:31:49 +00:00
|
|
|
Returns:
|
|
|
|
bool: True if file was reassembled correctly, else False
|
|
|
|
"""
|
|
|
|
return self.vessel.connection.assertComplete(self, allow_retry)
|
|
|
|
|
|
|
|
def compileComplete(self) -> None:
|
|
|
|
"""Reassemble a complete File from the uploaded Chunks
|
|
|
|
"""
|
2021-11-20 14:40:07 +00:00
|
|
|
self.vessel.connection.compileComplete(self)
|
|
|
|
|
2021-11-25 15:31:49 +00:00
|
|
|
def getChunk(self, count: int):
|
|
|
|
"""Get a Chunk of the source file
|
|
|
|
|
|
|
|
Args:
|
|
|
|
count (int): Number of the Chunk to generate
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
classes.chunk.Chunk: A Chunk object containing the portion of the
|
|
|
|
File object beginning at (count * chunksize) bytes and ending at
|
|
|
|
((count + 1) * chunksize - 1) bytes, with chunksize taken from the
|
|
|
|
RemoteFile initialization value
|
|
|
|
"""
|
2021-11-25 09:40:25 +00:00
|
|
|
return self.file.getChunk(count, self.chunksize)
|
2021-11-25 18:03:58 +00:00
|
|
|
|
|
|
|
def finalizeUpload(self) -> None:
|
|
|
|
"""Move complete file to its final destination and clean up
|
|
|
|
"""
|
|
|
|
self.vessel.connection.moveComplete(self)
|
|
|
|
self.vessel.connection.clearTempDir()
|