|
| 1 | +import os |
| 2 | +import zipfile |
| 3 | +import argparse |
| 4 | +import math |
| 5 | +import glob |
| 6 | + |
| 7 | +CHUNK_SIZE = 100 * 1024 * 1024 # 100 MB |
| 8 | + |
| 9 | +def compress_directory(source_dir, output_dir): |
| 10 | + os.makedirs(output_dir, exist_ok=True) |
| 11 | + temp_zip = os.path.join(output_dir, "temp.zip") |
| 12 | + |
| 13 | + print(f"Zipping {source_dir} into {temp_zip}...") |
| 14 | + with zipfile.ZipFile(temp_zip, 'w', zipfile.ZIP_DEFLATED) as zipf: |
| 15 | + for foldername, subfolders, filenames in os.walk(source_dir): |
| 16 | + for filename in filenames: |
| 17 | + filepath = os.path.join(foldername, filename) |
| 18 | + arcname = os.path.relpath(filepath, start=source_dir) |
| 19 | + zipf.write(filepath, arcname) |
| 20 | + |
| 21 | + total_size = os.path.getsize(temp_zip) |
| 22 | + num_parts = math.ceil(total_size / CHUNK_SIZE) |
| 23 | + print(f"Splitting into {num_parts} parts...") |
| 24 | + |
| 25 | + with open(temp_zip, 'rb') as f: |
| 26 | + for i in range(num_parts): |
| 27 | + chunk_data = f.read(CHUNK_SIZE) |
| 28 | + part_filename = os.path.join(output_dir, f"part{str(i+1).zfill(2)}.zip") |
| 29 | + with open(part_filename, 'wb') as chunk_file: |
| 30 | + chunk_file.write(chunk_data) |
| 31 | + print(f"Created: {part_filename}") |
| 32 | + |
| 33 | + os.remove(temp_zip) |
| 34 | + print("Temporary file removed. Done.") |
| 35 | + |
| 36 | +def extract_chunks(input_dir, output_dir): |
| 37 | + os.makedirs(output_dir, exist_ok=True) |
| 38 | + part_files = sorted(glob.glob(os.path.join(input_dir, "part*.zip"))) |
| 39 | + if not part_files: |
| 40 | + print("No parts found in the specified directory.") |
| 41 | + return |
| 42 | + |
| 43 | + reassembled = os.path.join(input_dir, "reassembled.zip") |
| 44 | + |
| 45 | + print(f"Found parts: {part_files}") |
| 46 | + with open(reassembled, 'wb') as output: |
| 47 | + for part in part_files: |
| 48 | + with open(part, 'rb') as pf: |
| 49 | + output.write(pf.read()) |
| 50 | + print(f"Reassembled into: {reassembled}") |
| 51 | + |
| 52 | + with zipfile.ZipFile(reassembled, 'r') as zipf: |
| 53 | + zipf.extractall(output_dir) |
| 54 | + print(f"Extracted to: {output_dir}") |
| 55 | + |
| 56 | + os.remove(reassembled) |
| 57 | + print("Removed reassembled zip.") |
| 58 | + |
| 59 | +def main(): |
| 60 | + parser = argparse.ArgumentParser(description="Chunked Zip Tool") |
| 61 | + subparsers = parser.add_subparsers(dest='command') |
| 62 | + |
| 63 | + compress_parser = subparsers.add_parser('compress') |
| 64 | + compress_parser.add_argument('source_dir', help="Directory to compress") |
| 65 | + compress_parser.add_argument('output_dir', help="Directory to save chunked zip parts") |
| 66 | + |
| 67 | + extract_parser = subparsers.add_parser('extract') |
| 68 | + extract_parser.add_argument('input_dir', help="Directory containing chunked parts") |
| 69 | + extract_parser.add_argument('output_dir', help="Directory to extract contents to") |
| 70 | + |
| 71 | + args = parser.parse_args() |
| 72 | + |
| 73 | + if args.command == 'compress': |
| 74 | + compress_directory(args.source_dir, args.output_dir) |
| 75 | + elif args.command == 'extract': |
| 76 | + extract_chunks(args.input_dir, args.output_dir) |
| 77 | + else: |
| 78 | + parser.print_help() |
| 79 | + |
| 80 | +if __name__ == "__main__": |
| 81 | + main() |
| 82 | + |
| 83 | +# python chunk_zipper.py compress ./windows ./win_c |
| 84 | +# python chunk_zipper.py extract ./win_c ./windows |
0 commit comments