class ZipGenerator(object): """ Generates the zip. Acts as context manager to ensure that all temporary files are deleted after usage. """ def __init__(self): self.empty = True def __enter__(self): self.tmp_file = NamedTemporaryFile(prefix="plone_zipexport_") self.tmp_file.__enter__() self.zip_file = ZipFile(self.tmp_file.name, "w", allowZip64=True) self.zip_file.__enter__() return self def __exit__(self, exc_type, exc_value, traceback): self.zip_file.__exit__(exc_type, exc_value, traceback) self.tmp_file.__exit__(exc_type, exc_value, traceback) def add_file(self, file_path, file_pointer): # paths in zipfile do not have a / at the root file_path = file_path.strip('/') file_path = self.generate_unique_filepath(file_path) try: self.zip_file.writefile(file_pointer, file_path) except RuntimeError: raise StandardError("ZipFile already generated/closed. " "Please add all files before generating.") self.empty = False def generate_unique_filepath(self, file_path): if file_path not in self.zip_file.namelist(): return file_path path, name = os.path.split(file_path) name, ext = os.path.splitext(name) for i in xrange(2, sys.maxint): new_filename = os.path.join(path, '%s (%d)%s' % (name, i, ext)) if new_filename not in self.zip_file.namelist(): return new_filename @property def is_empty(self): return self.empty def generate(self): if self.tmp_file is None: raise StandardError( "Please use ZipGenerator as a context manager.") self.zip_file.close() return self.tmp_file
class ZipGenerator(object): """ Generates the zip. Acts as context manager to ensure that all temporary files are deleted after usage. """ def __init__(self, path_normalizer=normalize_path): self.empty = True self.path_normalizer = path_normalizer def __enter__(self): self.tmp_file = NamedTemporaryFile(prefix="plone_zipexport_") self.tmp_file.__enter__() self.zip_file = ZipFile(self.tmp_file.name, "w", allowZip64=True) self.zip_file.__enter__() return self def __exit__(self, exc_type, exc_value, traceback): self.zip_file.__exit__(exc_type, exc_value, traceback) self.tmp_file.__exit__(exc_type, exc_value, traceback) def add_folder(self, folder_path): folder_path = safe_unicode(folder_path) # Always add a slash at the end of the path folder_path = u'{}/'.format(folder_path.strip('/')) # Creates a new empty folder self.zip_file.writestr(zipfile.ZipInfo(folder_path), '') self.empty = False def add_file(self, file_path, file_pointer): if self.path_normalizer is not None: file_path = self.path_normalizer(file_path) else: file_path = safe_unicode(file_path) # paths in zipfile do not have a / at the root file_path = file_path.strip('/') file_path = self.generate_unique_filepath(file_path) if not self.check_disk_has_space_for_file(file_pointer): raise NotEnoughSpaceOnDiskException() try: self.zip_file.writefile(file_pointer, file_path) except RuntimeError: raise StandardError("ZipFile already generated/closed. " "Please add all files before generating.") self.empty = False def generate_unique_filepath(self, file_path): if file_path not in self.zip_file.namelist(): return file_path path, name = os.path.split(file_path) name, ext = os.path.splitext(name) for i in xrange(2, sys.maxint): new_filename = os.path.join(path, '%s (%d)%s' % (name, i, ext)) if new_filename not in self.zip_file.namelist(): return new_filename def check_disk_has_space_for_file(self, file_d): disk_stat = os.statvfs(self.tmp_file.name) bytes_free = disk_stat.f_frsize * disk_stat.f_bavail position = file_d.tell() file_d.seek(0, os.SEEK_END) file_size = file_d.tell() - position file_d.seek(position) return file_size < bytes_free @property def is_empty(self): return self.empty def generate(self): if self.tmp_file is None: raise StandardError( "Please use ZipGenerator as a context manager.") self.zip_file.close() return self.tmp_file