Created
July 9, 2025 11:25
-
-
Save ashnair1/c7b88915b7c91d393d1e7e31c48b71c3 to your computer and use it in GitHub Desktop.
Python script to upload 3D Tiles.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import os | |
| import argparse | |
| from minio import Minio | |
| from minio.error import S3Error | |
| from dotenv import load_dotenv | |
| import urllib3 | |
| from tqdm import tqdm | |
| # Disable SSL verification warnings (internal use only) | |
| urllib3.disable_warnings() | |
| load_dotenv() | |
| # -------------------------------------- | |
| # ✅ ONLY MODIFY THESE TWO | |
| LOCAL_DIR = "/Users/ashwin.nair/Desktop/Projects/minio-nginx-3d-serve/AUH_Untextured_Filtered" | |
| REMOTE_DIR = "3dtiles/AUH_Untextured_Filtered" | |
| # -------------------------------------- | |
| # Connect to MinIO | |
| minio_client = Minio( | |
| endpoint=os.environ["MINIO_ENDPOINT"], | |
| access_key=os.environ["MINIO_ACCESS_KEY"], | |
| secret_key=os.environ["MINIO_SECRET_KEY"], | |
| secure=False, | |
| http_client=urllib3.PoolManager(cert_reqs="CERT_NONE"), | |
| ) | |
| def folder_exists_in_minio(bucket, remote_prefix): | |
| """Check if any object exists under this folder prefix""" | |
| objects = minio_client.list_objects(bucket, prefix=remote_prefix, recursive=True) | |
| return any(True for _ in objects) | |
| def upload_folder( | |
| bucket, local_data_root, remote_data_root, folder_name, dry_run=False, verbose=False | |
| ): | |
| """Upload all files in a folder to MinIO bucket""" | |
| local_folder = os.path.join(local_data_root, folder_name) | |
| # Count files first to show progress | |
| file_list = [] | |
| for root, _, files in os.walk(local_folder): | |
| for file in files: | |
| full_path = os.path.join(root, file) | |
| rel_path = os.path.relpath(full_path, local_data_root) | |
| dst_path = os.path.join(remote_data_root, rel_path).replace("\\", "/") | |
| file_list.append((full_path, rel_path, dst_path)) | |
| with tqdm( | |
| total=len(file_list), | |
| desc=f"{'DRY RUN' if dry_run else 'Uploading'} {folder_name}", | |
| unit="file", | |
| ) as pbar: | |
| for full_path, rel_path, dst_path in file_list: | |
| if dry_run: | |
| if verbose: | |
| print(f"[DRY RUN] Would upload {rel_path} to {dst_path}") | |
| else: | |
| try: | |
| minio_client.fput_object(bucket, dst_path, full_path) | |
| if verbose: | |
| print(f"Uploaded {rel_path} to {dst_path}") | |
| except S3Error as e: | |
| print(f"Error uploading to {dst_path}: {e}") | |
| pbar.update(1) | |
| def main(bucket, dry_run=False, verbose=False): | |
| if not minio_client.bucket_exists(bucket): | |
| print(f"❌ Error: Bucket '{bucket}' does not exist.") | |
| return | |
| local_data_path = os.path.join(LOCAL_DIR, "data") | |
| remote_data_path = os.path.join(REMOTE_DIR, "data").replace("\\", "/") | |
| if not os.path.isdir(local_data_path): | |
| print(f"❌ Error: '{local_data_path}' does not exist or is not a directory.") | |
| return | |
| for folder_name in os.listdir(local_data_path): | |
| local_folder_path = os.path.join(local_data_path, folder_name) | |
| if not os.path.isdir(local_folder_path): | |
| continue | |
| remote_prefix = f"{remote_data_path}/{folder_name}/" | |
| if folder_exists_in_minio(bucket, remote_prefix): | |
| if verbose: | |
| print(f"⏭️ Skipping existing folder: {folder_name}") | |
| continue | |
| if verbose: | |
| print( | |
| f"{'[DRY RUN] Would upload' if dry_run else 'Uploading'} folder: {folder_name}" | |
| ) | |
| upload_folder( | |
| bucket, | |
| local_data_path, | |
| remote_data_path, | |
| folder_name, | |
| dry_run=dry_run, | |
| verbose=verbose, | |
| ) | |
| #break | |
| if __name__ == "__main__": | |
| parser = argparse.ArgumentParser( | |
| description="Upload 3D tiles to MinIO with dry run and verbosity support" | |
| ) | |
| parser.add_argument( | |
| "--bucket", default="digitaltwin-public", help="MinIO bucket name" | |
| ) | |
| parser.add_argument( | |
| "--dry-run", action="store_true", help="Simulate upload without making changes" | |
| ) | |
| parser.add_argument( | |
| "--verbose", action="store_true", help="Enable detailed logging" | |
| ) | |
| args = parser.parse_args() | |
| main(bucket=args.bucket, dry_run=args.dry_run, verbose=args.verbose) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment